Handle a parameter to property behaviors.

For prototyping purposes, we only support a single trailing closure parameter to begin with.
This commit is contained in:
Joe Groff
2016-02-29 11:28:34 -08:00
parent 2da687b6e4
commit a4fa786a6f
12 changed files with 286 additions and 168 deletions

View File

@@ -3609,17 +3609,16 @@ enum class AccessStrategy : unsigned char {
struct BehaviorRecord {
// The behavior name.
TypeRepr *ProtocolName;
// The parameter function, if any.
FuncDecl *Param;
// The parameter expression, if any.
Expr *Param;
Optional<NormalProtocolConformance *> Conformance = None;
// The 'value' property from the behavior protocol that provides the property
// implementation.
VarDecl *ValueDecl = nullptr;
BehaviorRecord(TypeRepr *ProtocolName,
FuncDecl *Param)
Expr *Param)
: ProtocolName(ProtocolName), Param(Param)
{}
@@ -3919,7 +3918,7 @@ public:
void setComputedSetter(FuncDecl *Set);
/// \brief Add a behavior to a property.
void addBehavior(TypeRepr *Type, FuncDecl *Param);
void addBehavior(TypeRepr *Type, Expr *Param);
/// \brief Set a materializeForSet accessor for this declaration.
///

View File

@@ -256,6 +256,9 @@ ERROR(addressor_with_getter,none,
ERROR(addressor_with_setter,none,
"%select{variable|subscript}0 cannot provide both 'address' and "
"a setter; use an ordinary getter instead", (unsigned))
ERROR(behavior_multiple_vars,none,
"applying property behavior with parameter to multiple variables is "
"not supported", ())
// Import
ERROR(decl_expected_module_name,none,

View File

@@ -1564,18 +1564,14 @@ ERROR(property_behavior_value_type_doesnt_match,none,
(Identifier, Type, Identifier, Type))
NOTE(property_behavior_value_decl_here,none,
"'value' property declared here", ())
ERROR(property_behavior_invalid_initialValue_reqt,none,
"property behavior %0 'initialValue' requirement must be static, "
"get-only, and of type 'Self.Value'", (Identifier))
ERROR(property_behavior_requires_initialValue,none,
"property behavior %0 requires an initializer in the declaration of %1",
ERROR(property_behavior_invalid_parameter_reqt,none,
"property behavior %0 has a 'parameter' requirement that is "
"static or generic", (Identifier))
ERROR(property_behavior_requires_parameter,none,
"property behavior %0 requires a parameter in the declaration of %1",
(Identifier, Identifier))
ERROR(property_behavior_requires_unique_initialValue,none,
"property behavior %0 cannot destructure an initializer expression in a "
"compound pattern",
(Identifier))
ERROR(property_behavior_invalid_initializer,none,
"initializer expression provided, but property behavior %0 does not "
ERROR(property_behavior_invalid_parameter,none,
"parameter expression provided, but property behavior %0 does not "
"use it",
(Identifier))

View File

@@ -41,7 +41,6 @@ IDENTIFIER(generate)
IDENTIFIER(Generator)
IDENTIFIER(hashValue)
IDENTIFIER(init)
IDENTIFIER(initialValue)
IDENTIFIER(initStorage)
IDENTIFIER(load)
IDENTIFIER(next)
@@ -50,6 +49,7 @@ IDENTIFIER(objectAtIndexedSubscript)
IDENTIFIER(objectForKeyedSubscript)
IDENTIFIER(ObjectiveC)
IDENTIFIER_(OptionalNilComparisonType)
IDENTIFIER(parameter)
IDENTIFIER(Protocol)
IDENTIFIER(rawValue)
IDENTIFIER(RawValue)

View File

@@ -2872,7 +2872,7 @@ void AbstractStorageDecl::setComputedSetter(FuncDecl *Set) {
}
void AbstractStorageDecl::addBehavior(TypeRepr *Type,
FuncDecl *Param) {
Expr *Param) {
assert(BehaviorInfo.getPointer() == nullptr && "already set behavior!");
auto mem = getASTContext().Allocate(sizeof(BehaviorRecord),
alignof(BehaviorRecord));

View File

@@ -3956,14 +3956,64 @@ ParserStatus Parser::parseDeclVar(ParseDeclOptions Flags,
if (consumeIf(tok::kw___behavior)) {
auto type = parseType(diag::expected_behavior_name,
/*handle completion*/ true);
// TODO: recovery. could scan to next closing bracket
if (type.isParseError())
return makeParserError();
if (type.hasCodeCompletion())
return makeParserCodeCompletionStatus();
// Parse a following trailing closure argument.
// FIXME: Handle generalized parameters.
Expr *paramExpr = nullptr;
if (Tok.is(tok::l_brace)) {
// Record the variables that we're trying to set up. This allows us
// to cleanly reject "var x = x" when "x" isn't bound to an enclosing
// decl (even though names aren't injected into scope when the parameter
// is parsed).
SmallVector<VarDecl *, 4> Vars;
Vars.append(DisabledVars.begin(), DisabledVars.end());
pattern->collectVariables(Vars);
llvm::SaveAndRestore<decltype(DisabledVars)>
RestoreCurVars(DisabledVars, Vars);
llvm::SaveAndRestore<decltype(DisabledVarReason)>
RestoreReason(DisabledVarReason, diag::var_init_self_referential);
// Set up a decl context for the closure.
// This will be recontextualized to a method we synthesize during
// type checking.
if (!CurDeclContext->isLocalContext() && !topLevelDecl && !initContext)
initContext = Context.createPatternBindingContext(CurDeclContext);
Optional<ParseFunctionBody> initParser;
Optional<ContextChange> topLevelParser;
if (topLevelDecl)
topLevelParser.emplace(*this, topLevelDecl,
&State->getTopLevelContext());
if (initContext)
initParser.emplace(*this, initContext);
auto closure = parseExprClosure();
usedInitContext = true;
if (closure.isParseError())
return makeParserError();
if (closure.hasCodeCompletion())
return makeParserCodeCompletionStatus();
paramExpr = closure.get();
}
unsigned numVars = 0;
pattern->forEachVariable([&](VarDecl *VD) {
VD->addBehavior(type.get(), nullptr);
++numVars;
// TODO: Support parameter closure with multiple vars. This is tricky
// since the behavior's parameter type may be dependent on the
// property type, so we'd need to clone the closure expr for each var
// to re-type-check it.
if (numVars > 1 && paramExpr) {
diagnose(paramExpr->getLoc(), diag::behavior_multiple_vars);
paramExpr = nullptr;
}
VD->addBehavior(type.get(), paramExpr);
});
// If we syntactically match the second decl-var production, with a
// var-get-set clause, parse the var-get-set clause.

View File

@@ -1293,75 +1293,159 @@ void TypeChecker::completePropertyBehaviorStorage(VarDecl *VD,
ConcreteDeclRef(Context, Storage->getSetter(), MemberSubs));
}
void TypeChecker::completePropertyBehaviorInitialValue(VarDecl *VD,
VarDecl *BehaviorInitialValue,
void TypeChecker::completePropertyBehaviorParameter(VarDecl *VD,
FuncDecl *BehaviorParameter,
NormalProtocolConformance *BehaviorConformance,
ArrayRef<Substitution> SelfInterfaceSubs,
ArrayRef<Substitution> SelfContextSubs) {
// Create a property to witness the requirement.
// Create a method to witness the requirement.
auto DC = VD->getDeclContext();
SmallString<64> NameBuf = VD->getName().str();
NameBuf += ".initialValue";
auto InitialValueName = Context.getIdentifier(NameBuf);
// TODO: non-static initialValue
assert(BehaviorInitialValue->isStatic());
auto *InitialValue = new (Context) VarDecl(/*static*/ true,
/*let*/ false,
VD->getLoc(),
InitialValueName,
SelfContextSubs[1].getReplacement(),
NameBuf += ".parameter";
auto ParameterBaseName = Context.getIdentifier(NameBuf);
// Substitute the requirement type into the conforming context.
auto sig = BehaviorConformance->getProtocol()->getGenericSignatureOfContext();
auto ParameterTy = BehaviorParameter->getInterfaceType()
->castTo<AnyFunctionType>()
->getResult();
TypeSubstitutionMap interfaceMap = sig->getSubstitutionMap(SelfInterfaceSubs);
auto SubstInterfaceTy = ParameterTy.subst(VD->getModuleContext(),
interfaceMap, SubstOptions());
assert(SubstInterfaceTy && "storage type substitution failed?!");
TypeSubstitutionMap contextMap = sig->getSubstitutionMap(SelfContextSubs);
auto SubstContextTy = ParameterTy.subst(VD->getModuleContext(),
contextMap, SubstOptions());
assert(SubstContextTy && "storage type substitution failed?!");
auto SubstBodyResultTy = SubstContextTy->castTo<AnyFunctionType>()
->getResult();
// Add the Self type back to the interface and context types.
if (DC->isTypeContext()) {
if (DC->isGenericContext()) {
auto genericSig = DC->getGenericSignatureOfContext();
SubstInterfaceTy = GenericFunctionType::get(genericSig,
DC->getSelfInterfaceType(),
SubstInterfaceTy,
AnyFunctionType::ExtInfo());
auto genericParams = DC->getGenericParamsOfContext();
SubstContextTy = PolymorphicFunctionType::get(DC->getSelfTypeInContext(),
SubstContextTy,
genericParams);
} else {
SubstInterfaceTy = FunctionType::get(DC->getSelfInterfaceType(),
SubstInterfaceTy);
SubstContextTy = FunctionType::get(DC->getSelfTypeInContext(),
SubstContextTy);
}
}
// Borrow the parameters from the requirement declaration.
SmallVector<ParameterList *, 2> ParamLists;
if (DC->isTypeContext()) {
auto self = new (Context) ParamDecl(/*let*/ true, SourceLoc(), SourceLoc(),
Identifier(), SourceLoc(),
Context.Id_self,
DC->getSelfTypeInContext(), DC);
self->setInterfaceType(DC->getSelfInterfaceType());
self->setImplicit();
ParamLists.push_back(ParameterList::create(Context, SourceLoc(),
self, SourceLoc()));
ParamLists.back()->get(0)->setImplicit();
}
assert(BehaviorParameter->getParameterLists().size() == 2);
SmallVector<ParamDecl *, 4> Params;
SmallVector<Identifier, 4> NameComponents;
auto *DeclaredParams = BehaviorParameter->getParameterList(1);
for (unsigned i : indices(*DeclaredParams)) {
auto declaredParam = DeclaredParams->get(i);
auto declaredParamTy = declaredParam->getInterfaceType();
auto interfaceTy = declaredParamTy.subst(DC->getParentModule(),
interfaceMap,
SubstOptions());
assert(interfaceTy);
auto contextTy = declaredParamTy.subst(DC->getParentModule(),
contextMap, SubstOptions());
assert(contextTy);
SmallString<64> ParamNameBuf;
{
llvm::raw_svector_ostream names(ParamNameBuf);
names << "%arg." << i;
}
auto param = new (Context) ParamDecl(/*let*/ true, SourceLoc(), SourceLoc(),
Identifier(),
SourceLoc(),
Context.getIdentifier(ParamNameBuf),
contextTy, DC);
param->setInterfaceType(interfaceTy);
param->setImplicit();
Params.push_back(param);
NameComponents.push_back(Identifier());
}
ParamLists.push_back(ParameterList::create(Context, Params));
auto *Parameter = FuncDecl::create(Context, SourceLoc(),
StaticSpellingKind::None,
SourceLoc(),
DeclName(Context, ParameterBaseName,
NameComponents),
SourceLoc(), SourceLoc(),
SourceLoc(), nullptr, SubstContextTy,
ParamLists,
TypeLoc::withoutLoc(SubstBodyResultTy),
DC);
InitialValue->setInterfaceType(SelfInterfaceSubs[1].getReplacement());
InitialValue->setUserAccessible(false);
// Mark the vardecl to be final, implicit, and private. In a class, this
Parameter->setInterfaceType(SubstInterfaceTy);
// Mark the method to be final, implicit, and private. In a class, this
// prevents it from being dynamically dispatched.
if (VD->getDeclContext()->getAsClassOrClassExtensionContext())
makeFinal(Context, InitialValue);
InitialValue->setImplicit();
InitialValue->setAccessibility(Accessibility::Private);
InitialValue->setIsBeingTypeChecked();
addMemberToContextIfNeeded(InitialValue, DC);
Pattern *InitialPBDPattern = new (Context) NamedPattern(InitialValue,
/*implicit*/true);
InitialPBDPattern = new (Context) TypedPattern(InitialPBDPattern,
TypeLoc::withoutLoc(SelfContextSubs[1].getReplacement()),
/*implicit*/ true);
auto *InitialPBD = PatternBindingDecl::create(Context,
/*staticloc*/SourceLoc(),
StaticSpellingKind::KeywordStatic,
VD->getLoc(),
InitialPBDPattern, nullptr,
VD->getDeclContext());
InitialPBD->setImplicit();
InitialPBD->setInitializerChecked(0);
addMemberToContextIfNeeded(InitialPBD, VD->getDeclContext(), VD);
// Create a getter.
auto Get = createGetterPrototype(InitialValue, *this);
addMemberToContextIfNeeded(Get, DC);
// Take the initializer from the PatternBindingDecl for VD.
auto *InitValue = VD->getParentInitializer();
auto PBD = VD->getParentPatternBinding();
unsigned entryIndex = PBD->getPatternEntryIndexForVarDecl(VD);
PBD->setInit(entryIndex, nullptr);
PBD->setInitializerChecked(entryIndex);
if (DC->getAsClassOrClassExtensionContext())
makeFinal(Context, Parameter);
Parameter->setImplicit();
Parameter->setAccessibility(Accessibility::Private);
Parameter->setIsBeingTypeChecked();
Parameter->setBodyResultType(SubstBodyResultTy);
// Recontextualize any closure declcontexts nested in the initializer to
// realize that they are in the getter function.
InitValue->walk(RecontextualizeClosures(Get));
// realize that they are in the parameter function.
assert(VD->getBehavior()->Param);
VD->getBehavior()->Param->walk(RecontextualizeClosures(Parameter));
// Apply and return the closure in the function context.
SmallVector<Expr *, 4> argRefs;
SmallVector<Identifier, 4> argNames;
for (unsigned i : indices(Params)) {
auto param = Params[i];
auto expr = new (Context) DeclRefExpr(param, DeclNameLoc(),
/*implicit*/ true);
argRefs.push_back(expr);
argNames.push_back(DeclaredParams->get(i)->getName());
}
auto argTuple = TupleExpr::create(Context, SourceLoc(), argRefs,
argNames,
{}, SourceLoc(),
/*trailing closure*/ false,
/*implicit*/ true);
auto apply = new (Context) CallExpr(VD->getBehavior()->Param, argTuple,
/*implicit*/ true);
// Return the expression value.
auto Ret = new (Context) ReturnStmt(SourceLoc(), InitValue,
auto Ret = new (Context) ReturnStmt(SourceLoc(), apply,
/*implicit*/ true);
auto Body = BraceStmt::create(Context, SourceLoc(), ASTNode(Ret),
SourceLoc(), /*implicit*/ true);
Get->setBody(Body);
Parameter->setBody(Body);
InitialValue->makeComputed(SourceLoc(), Get, nullptr, nullptr, SourceLoc());
InitialValue->setIsBeingTypeChecked(false);
Parameter->setIsBeingTypeChecked(false);
typeCheckDecl(Parameter, true);
typeCheckDecl(Parameter, false);
addMemberToContextIfNeeded(Parameter, DC);
// Add the witnesses to the conformance.
ArrayRef<Substitution> MemberSubs;
@@ -1370,10 +1454,8 @@ void TypeChecker::completePropertyBehaviorInitialValue(VarDecl *VD,
->getForwardingSubstitutions(Context);
}
BehaviorConformance->setWitness(BehaviorInitialValue,
ConcreteDeclRef(Context, InitialValue, MemberSubs));
BehaviorConformance->setWitness(BehaviorInitialValue->getGetter(),
ConcreteDeclRef(Context, Get, MemberSubs));
BehaviorConformance->setWitness(BehaviorParameter,
ConcreteDeclRef(Context, Parameter, MemberSubs));
}
void TypeChecker::completePropertyBehaviorAccessors(VarDecl *VD,

View File

@@ -2641,6 +2641,8 @@ static void checkVarBehavior(VarDecl *decl, TypeChecker &TC) {
//
// It'd probably be cleaner to model as `where Self: ...` constraints when
// we have those.
//
// TODO: Handle non-protocol requirements ('class', base class, etc.)
for (auto refinedProto : behaviorProto->getInheritedProtocols(&TC)) {
ProtocolConformance *inherited = nullptr;
@@ -2772,7 +2774,7 @@ static void checkVarBehavior(VarDecl *decl, TypeChecker &TC) {
// Now that type witnesses are done, satisfy property and method requirements.
conformance->setState(ProtocolConformanceState::Checking);
bool requiresInitialValue = false;
bool requiresParameter = false;
for (auto requirementDecl : behaviorProto->getMembers()) {
auto requirement = dyn_cast<ValueDecl>(requirementDecl);
if (!requirement)
@@ -2783,6 +2785,8 @@ static void checkVarBehavior(VarDecl *decl, TypeChecker &TC) {
if (auto varReqt = dyn_cast<VarDecl>(requirement)) {
// Match a storage requirement.
if (varReqt->getName() == TC.Context.Id_storage) {
TC.validateDecl(varReqt);
auto storageTy = varReqt->getInterfaceType();
auto expectedInitStorageTy =
FunctionType::get(TC.Context.TheEmptyTupleType, storageTy);
@@ -2814,6 +2818,7 @@ static void checkVarBehavior(VarDecl *decl, TypeChecker &TC) {
TC.diagnose(foundFunc->getLoc(),
diag::property_behavior_protocol_reqt_here,
TC.Context.Id_initStorage);
conformance->setInvalid();
break;
}
@@ -2828,6 +2833,7 @@ static void checkVarBehavior(VarDecl *decl, TypeChecker &TC) {
for (auto found : lookup)
TC.diagnose(found.Decl->getLoc(),
diag::found_candidate);
conformance->setInvalid();
continue;
}
@@ -2849,67 +2855,56 @@ static void checkVarBehavior(VarDecl *decl, TypeChecker &TC) {
interfaceSubs,
contextSubs);
continue;
// Handle an initialValue requirement.
} else if (varReqt->getName() == TC.Context.Id_initialValue) {
requiresInitialValue = true;
// The requirement should be static, get-only, and have the type of the
// Value.
// TODO: A "deferred" initialization would be an instance requirement.
Type valueTy = GenericTypeParamType::get(0, 0, TC.Context);
assert(valueReqt && "no Value associated type?!");
valueTy = DependentMemberType::get(valueTy, valueReqt, TC.Context);
if (!varReqt->isStatic()
|| varReqt->isSettable(dc)
|| !varReqt->getInterfaceType()->isEqual(valueTy)) {
TC.diagnose(behavior->getLoc(),
diag::property_behavior_invalid_initialValue_reqt,
behaviorProto->getName());
TC.diagnose(varReqt->getLoc(),
diag::property_behavior_protocol_reqt_here,
TC.Context.Id_initialValue);
continue;
}
// The declaration must have an initializer expression.
// FIXME: It should have the initializer to itself too, no 'var (a,b)=x'
if (!decl->getParentInitializer()) {
TC.diagnose(behavior->getLoc(),
diag::property_behavior_requires_initialValue,
behaviorProto->getName(),
decl->getName());
continue;
}
if (!isa<NamedPattern>(
decl->getParentPattern()->getSemanticsProvidingPattern())) {
TC.diagnose(decl->getParentInitializer()->getLoc(),
diag::property_behavior_requires_unique_initialValue,
behaviorProto->getName());
continue;
}
// TODO: Support initialValue requirements in non-type contexts.
if (!dc->isTypeContext()) {
TC.diagnose(behavior->getLoc(),
diag::property_behavior_with_feature_not_supported,
behaviorProto->getName(), "initial value requirement");
conformance->setInvalid();
continue;
}
// Wrap the initializer expression in a get-only property to
TC.completePropertyBehaviorInitialValue(decl, varReqt,
conformance,
interfaceSubs,
contextSubs);
continue;
}
} else if (auto func = dyn_cast<FuncDecl>(requirement)) {
// Handle accessors as part of their property.
if (func->isAccessor())
continue;
// Handle a parameter block requirement.
if (func->getName() == TC.Context.Id_parameter) {
requiresParameter = true;
TC.validateDecl(func);
// The requirement should be for a nongeneric instance method.
if (func->isStatic() || func->isGeneric()) {
TC.diagnose(behavior->getLoc(),
diag::property_behavior_invalid_parameter_reqt,
behaviorProto->getName());
TC.diagnose(varReqt->getLoc(),
diag::property_behavior_protocol_reqt_here,
TC.Context.Id_parameter);
conformance->setInvalid();
continue;
}
// The declaration must have a parameter.
if (!decl->getBehavior()->Param) {
TC.diagnose(behavior->getLoc(),
diag::property_behavior_requires_parameter,
behaviorProto->getName(),
decl->getName());
conformance->setInvalid();
continue;
}
// TODO: Support parameter requirements in non-type contexts.
if (!dc->isTypeContext()) {
TC.diagnose(behavior->getLoc(),
diag::property_behavior_with_feature_not_supported,
behaviorProto->getName(), "parameter requirement");
conformance->setInvalid();
continue;
}
// Wrap the initializer expression in a get-only property to
TC.completePropertyBehaviorParameter(decl, func,
conformance,
interfaceSubs,
contextSubs);
continue;
}
}
unknownRequirement(requirement);
@@ -2919,9 +2914,9 @@ static void checkVarBehavior(VarDecl *decl, TypeChecker &TC) {
// use it, complain.
// TODO: The initializer could eventually be consumed by DI-style
// initialization.
if (!requiresInitialValue && decl->getParentInitializer()) {
TC.diagnose(decl->getParentInitializer()->getLoc(),
diag::property_behavior_invalid_initializer,
if (!requiresParameter && decl->getBehavior()->Param) {
TC.diagnose(decl->getBehavior()->Param->getLoc(),
diag::property_behavior_invalid_parameter,
behaviorProto->getName());
}

View File

@@ -1031,10 +1031,10 @@ public:
ArrayRef<Substitution> SelfInterfaceSubs,
ArrayRef<Substitution> SelfContextSubs);
/// Instantiate the initial value implementation for a behavior-backed
/// Instantiate the parameter implementation for a behavior-backed
/// property.
void completePropertyBehaviorInitialValue(VarDecl *VD,
VarDecl *BehaviorInitialValue,
void completePropertyBehaviorParameter(VarDecl *VD,
FuncDecl *BehaviorParameter,
NormalProtocolConformance *BehaviorConformance,
ArrayRef<Substitution> SelfInterfaceSubs,
ArrayRef<Substitution> SelfContextSubs);

View File

@@ -38,7 +38,7 @@ extension delayedImmutable {
protocol lazy {
associatedtype Value
var storage: Value? { get set }
static var initialValue: Value { get }
func parameter() -> Value
}
extension lazy {
var value: Value {
@@ -46,7 +46,7 @@ extension lazy {
if let existing = storage {
return existing
}
let value = Self.initialValue
let value = parameter()
storage = value
return value
}
@@ -69,7 +69,7 @@ func evaluateLazy() -> Int {
class Foo {
var x: Int __behavior delayedImmutable
var y = evaluateLazy() __behavior lazy
var y: Int __behavior lazy { evaluateLazy() }
}
var DelayedImmutable = TestSuite("DelayedImmutable")

View File

@@ -120,7 +120,7 @@ func exerciseStorage<T>(inout _ sx: S2<T>, inout _ sy: S2<Int>,
protocol withInit {
associatedtype Value
var storage: Value? { get set }
static var initialValue: Value { get }
func parameter() -> Value
}
extension withInit {
var value: Value {
@@ -131,14 +131,14 @@ extension withInit {
static func initStorage() -> Value? { }
}
// TODO: initialValue behaviors in non-instance context
// TODO: parameterized behaviors in non-instance context
func any<T>() -> T { }
struct S3<T> {
var instance: T = any() __behavior withInit
var instance: T __behavior withInit { any() }
}
class C3<T> {
var instance: T = any() __behavior withInit
var instance: T __behavior withInit { any() }
}
func exerciseStorage<T>(inout _ sx: S3<T>, inout _ sy: S3<Int>,

View File

@@ -184,44 +184,37 @@ struct S<T> {
static var testGetset: T __behavior getset
}
protocol initialValue {
protocol parameterized {
associatedtype Value
static var initialValue: Value { get }
func parameter() -> Value
}
extension initialValue {
extension parameterized {
var value: Value {
get { }
set { }
}
}
let compoundInitialValue = (0,0)
struct TestInitialValues {
var hasInitialValue: Int = 0 __behavior initialValue
var (sharedInitialValue1, sharedInitialValue2): (Int,Int)
= compoundInitialValue __behavior initialValue // expected-error * {{cannot destructure}}
var missingInitialValue: Int __behavior initialValue // expected-error{{requires an initializer}}
// TODO: "return expression" message is wrong
var invalidInitialValue: Int = 5.5 __behavior initialValue // expected-error{{cannot convert return expression of type 'Double' to return type 'Int'}}
struct TestParameters {
var hasParameter: Int __behavior parameterized { 0 }
var (sharedParameter1, sharedParameter2): (Int,Int)
__behavior parameterized { 0 } // expected-error {{multiple variables is not supported}} expected-error{{sharedParameter2}}
var missingParameter: Int __behavior parameterized // expected-error{{requires a parameter}}
var invalidParameter: Int __behavior parameterized { 5.5 } // expected-error{{cannot convert return expression of type 'Double' to return type 'Int'}}
}
// TODO
var globalInitialValue: Int = 0 __behavior initialValue // expected-error{{not supported}}
var globalParameter: Int __behavior parameterized { 0 } // expected-error{{not supported}}
protocol noInitialValue {
protocol noParameter {
associatedtype Value
}
extension noInitialValue {
extension noParameter {
var value: Value {
get { }
set { }
}
}
var hasNoInitialValue: Int __behavior noInitialValue
var hasUnwantedInitialValue: Int = 0 __behavior noInitialValue // expected-error{{initializer expression provided, but property behavior 'noInitialValue' does not use it}}
var (hasUnwantedSharedInitialValue1,
hasUnwantedSharedInitialValue2): (Int, Int)
= compoundInitialValue // expected-error * {{initializer expression provided, but property behavior 'noInitialValue' does not use it}}
__behavior noInitialValue
var hasNoParameter: Int __behavior noParameter
var hasUnwantedParameter: Int __behavior noParameter { 0 } // expected-error{{parameter expression provided, but property behavior 'noParameter' does not use it}}