mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
[SE-0258] Support initial values & explicit initialization arguments.
Allow both explicit initialization and initial values to work together, e.g., ``` @Clamping(min: 0, max: 255) var red: Int = 127 ``` gets initialized as ``` Clamping(initialValue: 127, min: 0, max: 255) ```
This commit is contained in:
@@ -4430,12 +4430,6 @@ ERROR(property_with_wrapper_overrides,none,
|
||||
"property %0 with attached wrapper cannot override another property",
|
||||
(DeclName))
|
||||
|
||||
ERROR(property_wrapper_and_normal_init,none,
|
||||
"property %0 with attached wrapper cannot initialize both the "
|
||||
"wrapper type and the property", (DeclName))
|
||||
ERROR(property_wrapper_init_without_initial_value, none,
|
||||
"initializing property %0 with wrapper %1 that lacks "
|
||||
"an 'init(initialValue:)' initializer", (DeclName, Type))
|
||||
NOTE(property_wrapper_direct_init,none,
|
||||
"initialize the property wrapper type directly with "
|
||||
"'(...') on the attribute", ())
|
||||
|
||||
@@ -1847,8 +1847,8 @@ PropertyWrapperBackingPropertyInfoRequest::evaluate(Evaluator &evaluator,
|
||||
}
|
||||
|
||||
// Get the property wrapper information.
|
||||
if (!var->allAttachedPropertyWrappersHaveInitialValueInit()) {
|
||||
assert(!originalInitialValue);
|
||||
if (!var->allAttachedPropertyWrappersHaveInitialValueInit() &&
|
||||
!originalInitialValue) {
|
||||
return PropertyWrapperBackingPropertyInfo(
|
||||
backingVar, storageVar, nullptr, nullptr, nullptr);
|
||||
}
|
||||
@@ -1859,9 +1859,10 @@ PropertyWrapperBackingPropertyInfoRequest::evaluate(Evaluator &evaluator,
|
||||
new (ctx) OpaqueValueExpr(var->getLoc(), var->getType(),
|
||||
/*isPlaceholder=*/true);
|
||||
Expr *initializer = buildPropertyWrapperInitialValueCall(
|
||||
var, storageType, origValue);
|
||||
typeCheckSynthesizedWrapperInitializer(pbd, backingVar, parentPBD,
|
||||
initializer);
|
||||
var, storageType, origValue,
|
||||
/*ignoreAttributeArgs=*/!originalInitialValue);
|
||||
typeCheckSynthesizedWrapperInitializer(
|
||||
pbd, backingVar, parentPBD, initializer);
|
||||
|
||||
return PropertyWrapperBackingPropertyInfo(
|
||||
backingVar, storageVar, originalInitialValue, initializer, origValue);
|
||||
|
||||
@@ -2709,7 +2709,16 @@ bool TypeChecker::typeCheckBinding(Pattern *&pattern, Expr *&initializer,
|
||||
// call.
|
||||
auto &ctx = singleVar->getASTContext();
|
||||
auto outermostWrapperAttr = wrapperAttrs.front();
|
||||
if (auto outermostArg = outermostWrapperAttr->getArg()) {
|
||||
if (initializer) {
|
||||
// Form init(initialValue:) call(s).
|
||||
Expr *wrappedInitializer =
|
||||
buildPropertyWrapperInitialValueCall(
|
||||
singleVar, Type(), initializer, /*ignoreAttributeArgs=*/false);
|
||||
if (!wrappedInitializer)
|
||||
return;
|
||||
|
||||
initializer = wrappedInitializer;
|
||||
} else if (auto outermostArg = outermostWrapperAttr->getArg()) {
|
||||
Type outermostWrapperType =
|
||||
singleVar->getAttachedPropertyWrapperType(0);
|
||||
if (!outermostWrapperType)
|
||||
@@ -2718,63 +2727,14 @@ bool TypeChecker::typeCheckBinding(Pattern *&pattern, Expr *&initializer,
|
||||
auto typeExpr = TypeExpr::createImplicitHack(
|
||||
outermostWrapperAttr->getTypeLoc().getLoc(),
|
||||
outermostWrapperType, ctx);
|
||||
if (initializer) {
|
||||
singleVar->diagnose(diag::property_wrapper_and_normal_init,
|
||||
singleVar->getFullName())
|
||||
.highlight(outermostWrapperAttr->getRange())
|
||||
.highlight(initializer->getSourceRange());
|
||||
}
|
||||
|
||||
initializer = CallExpr::create(
|
||||
ctx, typeExpr, outermostArg,
|
||||
outermostWrapperAttr->getArgumentLabels(),
|
||||
outermostWrapperAttr->getArgumentLabelLocs(),
|
||||
/*hasTrailingClosure=*/false,
|
||||
/*implicit=*/false);
|
||||
} else if (singleVar->allAttachedPropertyWrappersHaveInitialValueInit()) {
|
||||
// FIXME: we want to use the initialValueInits we found.
|
||||
assert(initializer);
|
||||
|
||||
// Form init(initialValue:) call(s).
|
||||
Expr *wrappedInitializer =
|
||||
buildPropertyWrapperInitialValueCall(singleVar, Type(),
|
||||
initializer);
|
||||
if (!wrappedInitializer)
|
||||
return;
|
||||
|
||||
initializer = wrappedInitializer;
|
||||
} else {
|
||||
// Find the property wrapper that does not have an initialValue
|
||||
// initializer and diagnose it.
|
||||
for (unsigned i : indices(wrapperAttrs)) {
|
||||
auto wrapperTypeInfo =
|
||||
singleVar->getAttachedPropertyWrapperTypeInfo(i);
|
||||
if (wrapperTypeInfo.initialValueInit)
|
||||
continue;
|
||||
|
||||
Type wrapperType = singleVar->getAttachedPropertyWrapperType(i);
|
||||
if (!wrapperType)
|
||||
return;
|
||||
|
||||
auto wrapperNominal = wrapperType->getAnyNominal();
|
||||
if (!wrapperNominal)
|
||||
return;
|
||||
|
||||
CustomAttr *wrapperAttr = wrapperAttrs[i];
|
||||
singleVar->diagnose(diag::property_wrapper_init_without_initial_value,
|
||||
singleVar->getFullName(), wrapperType)
|
||||
.highlight(initializer->getSourceRange());
|
||||
ctx.Diags.diagnose(wrapperAttr->getLocation(),
|
||||
diag::property_wrapper_direct_init)
|
||||
.fixItInsertAfter(initializer->getSourceRange().End,
|
||||
"(<# initializer args #>)");
|
||||
wrapperNominal->diagnose(diag::kind_declname_declared_here,
|
||||
wrapperNominal->getDescriptiveKind(),
|
||||
wrapperNominal->getFullName());
|
||||
return;
|
||||
}
|
||||
|
||||
llvm_unreachable("All wrappers had init(initialValue:)?");
|
||||
llvm_unreachable("No initializer anywhere?");
|
||||
}
|
||||
wrapperAttrs[0]->setSemanticInit(initializer);
|
||||
|
||||
|
||||
@@ -560,7 +560,8 @@ Type swift::computeWrappedValueType(VarDecl *var, Type backingStorageType,
|
||||
}
|
||||
|
||||
Expr *swift::buildPropertyWrapperInitialValueCall(
|
||||
VarDecl *var, Type backingStorageType, Expr *value) {
|
||||
VarDecl *var, Type backingStorageType, Expr *value,
|
||||
bool ignoreAttributeArgs) {
|
||||
// From the innermost wrapper type out, form init(initialValue:) calls.
|
||||
ASTContext &ctx = var->getASTContext();
|
||||
auto wrapperAttrs = var->getAttachedPropertyWrappers();
|
||||
@@ -575,8 +576,39 @@ Expr *swift::buildPropertyWrapperInitialValueCall(
|
||||
auto typeExpr = TypeExpr::createImplicitHack(
|
||||
wrapperAttrs[i]->getTypeLoc().getLoc(),
|
||||
wrapperType, ctx);
|
||||
|
||||
// If there were no arguments provided for the attribute at this level,
|
||||
// call `init(initialValue:)` directly.
|
||||
auto attr = wrapperAttrs[i];
|
||||
if (!attr->getArg() || ignoreAttributeArgs) {
|
||||
initializer = CallExpr::createImplicit(
|
||||
ctx, typeExpr, {initializer}, {ctx.Id_initialValue});
|
||||
continue;
|
||||
}
|
||||
|
||||
// Splice `initialValue:` into the argument list.
|
||||
SmallVector<Expr *, 4> elements;
|
||||
SmallVector<Identifier, 4> elementNames;
|
||||
SmallVector<SourceLoc, 4> elementLocs;
|
||||
elements.push_back(initializer);
|
||||
elementNames.push_back(ctx.Id_initialValue);
|
||||
elementLocs.push_back(SourceLoc());
|
||||
|
||||
if (auto tuple = dyn_cast<TupleExpr>(attr->getArg())) {
|
||||
for (unsigned i : range(tuple->getNumElements())) {
|
||||
elements.push_back(tuple->getElement(i));
|
||||
elementNames.push_back(tuple->getElementName(i));
|
||||
elementLocs.push_back(tuple->getElementNameLoc(i));
|
||||
}
|
||||
} else {
|
||||
auto paren = cast<ParenExpr>(attr->getArg());
|
||||
elements.push_back(paren->getSubExpr());
|
||||
elementNames.push_back(Identifier());
|
||||
elementLocs.push_back(SourceLoc());
|
||||
}
|
||||
|
||||
initializer = CallExpr::createImplicit(
|
||||
ctx, typeExpr, elements, elementNames);
|
||||
}
|
||||
|
||||
return initializer;
|
||||
|
||||
@@ -2163,7 +2163,8 @@ Type computeWrappedValueType(VarDecl *var, Type backingStorageType,
|
||||
/// \c value as the original value.
|
||||
Expr *buildPropertyWrapperInitialValueCall(VarDecl *var,
|
||||
Type backingStorageType,
|
||||
Expr *value);
|
||||
Expr *value,
|
||||
bool ignoreAttributeArgs);
|
||||
|
||||
/// Whether an overriding declaration requires the 'override' keyword.
|
||||
enum class OverrideRequiresKeyword {
|
||||
|
||||
@@ -193,9 +193,9 @@ struct BadCombinations {
|
||||
}
|
||||
|
||||
struct MultipleWrappers {
|
||||
@Wrapper(wrappedValue: 17) // expected-error{{cannot convert value of type 'Wrapper<Int>' to specified type 'Int'}}
|
||||
@WrapperWithInitialValue
|
||||
var x: Int = 17 // expected-error{{property 'x' with attached wrapper cannot initialize both the wrapper type and the property}}
|
||||
@Wrapper(wrappedValue: 17)
|
||||
@WrapperWithInitialValue // expected-error{{extra argument 'initialValue' in call}}
|
||||
var x: Int = 17
|
||||
|
||||
@WrapperWithInitialValue // expected-error 2{{property wrapper can only apply to a single variable}}
|
||||
var (y, z) = (1, 2)
|
||||
@@ -213,7 +213,7 @@ struct Initialization {
|
||||
var x2: Double
|
||||
|
||||
@Wrapper(wrappedValue: 17)
|
||||
var x3 = 42 // expected-error{{property 'x3' with attached wrapper cannot initialize both the wrapper type and the property}}
|
||||
var x3 = 42 // expected-error{{extra argument 'initialValue' in call}}
|
||||
|
||||
@Wrapper(wrappedValue: 17)
|
||||
var x4
|
||||
@@ -232,6 +232,44 @@ struct Initialization {
|
||||
}
|
||||
}
|
||||
|
||||
@propertyWrapper
|
||||
struct Clamping<V: Comparable> {
|
||||
var value: V
|
||||
let min: V
|
||||
let max: V
|
||||
|
||||
init(initialValue: V, min: V, max: V) {
|
||||
value = initialValue
|
||||
self.min = min
|
||||
self.max = max
|
||||
assert(value >= min && value <= max)
|
||||
}
|
||||
|
||||
var wrappedValue: V {
|
||||
get { return value }
|
||||
set {
|
||||
if newValue < min {
|
||||
value = min
|
||||
} else if newValue > max {
|
||||
value = max
|
||||
} else {
|
||||
value = newValue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct Color {
|
||||
@Clamping(min: 0, max: 255) var red: Int = 127
|
||||
@Clamping(min: 0, max: 255) var green: Int = 127
|
||||
@Clamping(min: 0, max: 255) var blue: Int = 127
|
||||
@Clamping(min: 0, max: 255) var alpha: Int = 255
|
||||
}
|
||||
|
||||
func testColor() {
|
||||
_ = Color(green: 17)
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Wrapper type formation
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -732,14 +770,13 @@ struct UsesExplicitClosures {
|
||||
struct PD<Value> {
|
||||
var wrappedValue: Value
|
||||
|
||||
init<A>(initialValue: Value, a: A) { // expected-note{{'init(initialValue:a:)' declared here}}
|
||||
init<A>(initialValue: Value, a: A) {
|
||||
self.wrappedValue = initialValue
|
||||
}
|
||||
}
|
||||
|
||||
struct TestPD {
|
||||
@PD(a: "foo") var foo: Int = 42 // expected-error{{property 'foo' with attached wrapper cannot initialize both the wrapper type and the property}}
|
||||
// expected-error@-1{{missing argument for parameter 'initialValue' in call}}
|
||||
@PD(a: "foo") var foo: Int = 42
|
||||
}
|
||||
|
||||
protocol P { }
|
||||
|
||||
Reference in New Issue
Block a user