[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:
Doug Gregor
2019-06-14 01:10:53 -07:00
parent 99b40ba728
commit d35e1eaf72
6 changed files with 97 additions and 72 deletions

View File

@@ -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", ())

View File

@@ -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);

View File

@@ -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);

View File

@@ -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;

View File

@@ -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 {

View File

@@ -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 { }