Revert "SIL: Drop the upcast_existential* instructions."

This reverts commit r22345.

Swift SVN r22353
This commit is contained in:
Joe Groff
2014-09-29 13:46:41 +00:00
parent f33d3a7abd
commit 152aa9e244
23 changed files with 265 additions and 6 deletions

View File

@@ -2909,6 +2909,26 @@ initialized existential container can be destroyed with ``destroy_addr`` as
usual. It is undefined behavior to ``destroy_addr`` a partially-initialized
existential container.
upcast_existential
``````````````````
::
sil-instruction ::= 'upcast_existential' '[take]'? sil-operand
'to' sil-operand
upcast_existential %0 : $*protocol<P, Q> to %1 : $*P
// %0 must be the address of a non-class protocol or protocol composition
// type
// %1 must be the address of a non-class protocol or protocol composition
// type that is a supertype of %0
Initializes the memory referenced by the destination ``%1`` with the value
contained in the existing existential container referenced by ``%0``.
The ``[take]`` attribute may be applied to the instruction, in which case,
the source existential container is destroyed and ownership of the contained
value is taken by the destination. Without the ``[take]`` attribute, the
destination receives an independently-owned copy of the value.
deinit_existential
``````````````````
::
@@ -2998,6 +3018,20 @@ init_existential_ref
Creates a class existential container of type ``$P`` containing a reference to
the class instance ``%0``.
upcast_existential_ref
``````````````````````
::
sil-instruction ::= 'upcast_existential_ref' sil-operand 'to' sil-type
%1 = upcast_existential_ref %0 : $protocol<P, Q> to $P
// %0 must be of a class protocol or protocol composition type
// $P must be a class protocol or protocol composition type that is a
// supertype of %0's type
Converts a class existential container to a more general protocol or protocol
composition type.
project_existential_ref
```````````````````````
::

View File

@@ -370,6 +370,7 @@ UNARY_OP_MATCH_WITH_ARG_MATCHER(OpenExistentialRefInst)
UNARY_OP_MATCH_WITH_ARG_MATCHER(InitExistentialInst)
UNARY_OP_MATCH_WITH_ARG_MATCHER(InitExistentialRefInst)
UNARY_OP_MATCH_WITH_ARG_MATCHER(DeinitExistentialInst)
UNARY_OP_MATCH_WITH_ARG_MATCHER(UpcastExistentialRefInst)
UNARY_OP_MATCH_WITH_ARG_MATCHER(ProjectBlockStorageInst)
UNARY_OP_MATCH_WITH_ARG_MATCHER(StrongRetainInst)
UNARY_OP_MATCH_WITH_ARG_MATCHER(StrongReleaseInst)

View File

@@ -661,6 +661,22 @@ public:
Conformances, &F));
}
UpcastExistentialInst *createUpcastExistential(SILLocation Loc,
SILValue SrcExistential,
SILValue DestExistential,
IsTake_t isTakeOfSrc) {
return insert(new (F.getModule())
UpcastExistentialInst(Loc, SrcExistential, DestExistential,
isTakeOfSrc));
}
UpcastExistentialRefInst *createUpcastExistentialRef(SILLocation Loc,
SILValue Operand,
SILType Ty) {
return insert(new (F.getModule())
UpcastExistentialRefInst(Loc, Operand, Ty));
}
DeinitExistentialInst *createDeinitExistential(SILLocation Loc,
SILValue Existential) {
return insert(new (F.getModule()) DeinitExistentialInst(Loc, Existential));

View File

@@ -1027,6 +1027,26 @@ SILCloner<ImplClass>::visitDeinitExistentialInst(DeinitExistentialInst *Inst) {
getOpValue(Inst->getOperand())));
}
template<typename ImplClass>
void
SILCloner<ImplClass>::visitUpcastExistentialInst(UpcastExistentialInst *Inst) {
doPostProcess(Inst,
getBuilder().createUpcastExistential(getOpLocation(Inst->getLoc()),
getOpValue(Inst->getSrcExistential()),
getOpValue(Inst->getDestExistential()),
(IsTake_t)Inst->isTakeOfSrc()));
}
template<typename ImplClass>
void
SILCloner<ImplClass>::
visitUpcastExistentialRefInst(UpcastExistentialRefInst *Inst) {
doPostProcess(Inst,
getBuilder().createUpcastExistentialRef(getOpLocation(Inst->getLoc()),
getOpValue(Inst->getOperand()),
getOpType(Inst->getType())));
}
template<typename ImplClass>
void
SILCloner<ImplClass>::visitCopyBlockInst(CopyBlockInst *Inst) {

View File

@@ -2139,6 +2139,50 @@ public:
: UnaryInstructionBase(Loc, Existential) {}
};
/// UpcastExistentialInst - Copies the concrete value from an existential
/// container of a protocol type to another uninitialized existential container
/// for a supertype of the original protocol type. The destination can be
/// of a base protocol type or of a protocol composition that is a superset
/// of the original type (or a protocol composition of base protocols).
class UpcastExistentialInst : public SILInstruction {
unsigned IsTakeOfSrc : 1;
enum { SrcExistential, DestExistential };
FixedOperandList<2> Operands;
public:
UpcastExistentialInst(SILLocation Loc,
SILValue SrcExistential,
SILValue DestExistential,
IsTake_t isTakeOfSrc);
SILValue getSrcExistential() const { return Operands[SrcExistential].get(); }
SILValue getDestExistential() const { return Operands[DestExistential].get();}
/// True if the destination can take ownership of the concrete value from the
/// source.
IsTake_t isTakeOfSrc() const { return IsTake_t(IsTakeOfSrc); }
ArrayRef<Operand> getAllOperands() const { return Operands.asArray(); }
MutableArrayRef<Operand> getAllOperands() { return Operands.asArray(); }
static bool classof(const ValueBase *V) {
return V->getKind() == ValueKind::UpcastExistentialInst;
}
};
/// UpcastExistentialRefInst - Converts a value of class existential
/// container to another, more general class existential container type.
class UpcastExistentialRefInst
: public UnaryInstructionBase<ValueKind::UpcastExistentialRefInst,
ConversionInst>
{
public:
UpcastExistentialRefInst(SILLocation Loc,
SILValue Operand,
SILType DestTy)
: UnaryInstructionBase(Loc, Operand, DestTy)
{}
};
/// Projects the capture storage address from a @block_storage address.
class ProjectBlockStorageInst
: public UnaryInstructionBase<ValueKind::ProjectBlockStorageInst,

View File

@@ -159,10 +159,12 @@ ABSTRACT_VALUE(SILInstruction, ValueBase)
// Protocol and Protocol Composition Types
INST(InitExistentialInst, SILInstruction, MayWrite)
INST(UpcastExistentialInst, SILInstruction, MayWrite)
INST(DeinitExistentialInst, SILInstruction, MayHaveSideEffects)
INST(ProjectExistentialInst, SILInstruction, None)
INST(OpenExistentialInst, SILInstruction, None)
INST(InitExistentialRefInst, SILInstruction, None)
INST(UpcastExistentialRefInst, SILInstruction, None)
INST(ProjectExistentialRefInst, SILInstruction, None)
INST(OpenExistentialRefInst, SILInstruction, None)

View File

@@ -48,7 +48,7 @@ const uint16_t VERSION_MAJOR = 0;
/// Serialized module format minor version number.
///
/// When the format changes IN ANY WAY, this number should be incremented.
const uint16_t VERSION_MINOR = 148;
const uint16_t VERSION_MINOR = 147;
using DeclID = Fixnum<31>;
using DeclIDField = BCFixed<31>;

View File

@@ -646,6 +646,8 @@ public:
void visitOpenExistentialRefInst(OpenExistentialRefInst *i);
void visitInitExistentialInst(InitExistentialInst *i);
void visitInitExistentialRefInst(InitExistentialRefInst *i);
void visitUpcastExistentialInst(UpcastExistentialInst *i);
void visitUpcastExistentialRefInst(UpcastExistentialRefInst *i);
void visitDeinitExistentialInst(DeinitExistentialInst *i);
void visitProjectBlockStorageInst(ProjectBlockStorageInst *i);
@@ -3176,6 +3178,30 @@ void IRGenSILFunction::visitInitExistentialRefInst(InitExistentialRefInst *i) {
setLoweredExplosion(SILValue(i, 0), result);
}
void IRGenSILFunction::visitUpcastExistentialInst(
swift::UpcastExistentialInst *i) {
/// FIXME: Handle source existential being class existential.
Address src = getLoweredAddress(i->getSrcExistential());
Address dest = getLoweredAddress(i->getDestExistential());
SILType srcType = i->getSrcExistential().getType();
SILType destType = i->getDestExistential().getType();
emitOpaqueExistentialContainerUpcast(*this, dest, destType, src, srcType,
i->isTakeOfSrc());
}
void IRGenSILFunction::visitUpcastExistentialRefInst(
swift::UpcastExistentialRefInst *i) {
Explosion src = getLoweredExplosion(i->getOperand());
Explosion dest;
SILType srcType = i->getOperand().getType();
SILType destType = i->getType();
emitClassExistentialContainerUpcast(*this, dest, destType,
src, srcType);
setLoweredExplosion(SILValue(i, 0), dest);
}
void IRGenSILFunction::visitDeinitExistentialInst(
swift::DeinitExistentialInst *i) {
Address container = getLoweredAddress(i->getOperand());

View File

@@ -1238,6 +1238,8 @@ bool SILParser::parseSILOpcode(ValueKind &Opcode, SourceLoc &OpcodeLoc,
.Case("unowned_to_ref", ValueKind::UnownedToRefInst)
.Case("unreachable", ValueKind::UnreachableInst)
.Case("upcast", ValueKind::UpcastInst)
.Case("upcast_existential", ValueKind::UpcastExistentialInst)
.Case("upcast_existential_ref", ValueKind::UpcastExistentialRefInst)
.Default(ValueKind::SILArgument);
if (Opcode != ValueKind::SILArgument) {
@@ -1702,6 +1704,7 @@ bool SILParser::parseSILInstruction(SILBasicBlock *BB) {
case ValueKind::ThickToObjCMetatypeInst:
case ValueKind::ObjCToThickMetatypeInst:
case ValueKind::ConvertFunctionInst:
case ValueKind::UpcastExistentialRefInst:
case ValueKind::ObjCExistentialMetatypeToObjectInst:
case ValueKind::ObjCMetatypeToObjectInst: {
SILType Ty;
@@ -1771,6 +1774,9 @@ bool SILParser::parseSILInstruction(SILBasicBlock *BB) {
case ValueKind::ObjCToThickMetatypeInst:
ResultVal = B.createObjCToThickMetatype(InstLoc, Val, Ty);
break;
case ValueKind::UpcastExistentialRefInst:
ResultVal = B.createUpcastExistentialRef(InstLoc, Val, Ty);
break;
case ValueKind::ObjCMetatypeToObjectInst:
ResultVal = B.createObjCMetatypeToObject(InstLoc, Val, Ty);
break;
@@ -2444,6 +2450,26 @@ bool SILParser::parseSILInstruction(SILBasicBlock *BB) {
IsInitialization_t(IsInit));
break;
}
case ValueKind::UpcastExistentialInst: {
SILValue DestVal;
SourceLoc SrcLoc, DestLoc, ToLoc;
Identifier ToToken;
bool IsTake = false;
if (parseSILOptional(IsTake, *this, "take") ||
parseTypedValueRef(Val, SrcLoc) ||
parseSILIdentifier(ToToken, ToLoc,
diag::expected_tok_in_sil_instr, "to") ||
parseTypedValueRef(DestVal, DestLoc))
return true;
if (ToToken.str() != "to") {
P.diagnose(ToLoc, diag::expected_tok_in_sil_instr, "to");
return true;
}
ResultVal = B.createUpcastExistential(InstLoc, Val, DestVal,
IsTake_t(IsTake));
break;
}
case ValueKind::StructInst: {
SILType StructTy;
if (parseSILType(StructTy) ||

View File

@@ -472,6 +472,10 @@ namespace {
return true;
}
bool visitUpcastExistentialRefInst(UpcastExistentialRefInst *RHS) {
return true;
}
bool visitObjCMetatypeToObjectInst(ObjCMetatypeToObjectInst *RHS) {
return true;
}
@@ -1024,6 +1028,17 @@ OpenExistentialRefInst::OpenExistentialRefInst(SILLocation Loc,
: UnaryInstructionBase(Loc, Operand, Ty)
{}
UpcastExistentialInst::UpcastExistentialInst(SILLocation Loc,
SILValue SrcExistential,
SILValue DestExistential,
IsTake_t isTakeOfSrc)
: SILInstruction(ValueKind::UpcastExistentialInst, Loc),
IsTakeOfSrc(isTakeOfSrc),
Operands(this, SrcExistential, DestExistential)
{
}
//===----------------------------------------------------------------------===//
// Instructions representing terminators
//===----------------------------------------------------------------------===//

View File

@@ -850,6 +850,9 @@ public:
void visitObjCToThickMetatypeInst(ObjCToThickMetatypeInst *CI) {
printUncheckedConversionInst(CI, CI->getOperand(),"objc_to_thick_metatype");
}
void visitUpcastExistentialRefInst(UpcastExistentialRefInst *CI) {
printUncheckedConversionInst(CI, CI->getOperand(),"upcast_existential_ref");
}
void visitObjCMetatypeToObjectInst(ObjCMetatypeToObjectInst *CI) {
printUncheckedConversionInst(CI, CI->getOperand(),
"objc_metatype_to_object");
@@ -1044,6 +1047,13 @@ public:
<< " : $" << AEI->getFormalConcreteType()
<< ", " << AEI->getType();
}
void visitUpcastExistentialInst(UpcastExistentialInst *UEI) {
OS << "upcast_existential ";
if (UEI->isTakeOfSrc())
OS << "[take] ";
OS << getIDAndType(UEI->getSrcExistential())
<< " to " << getIDAndType(UEI->getDestExistential());
}
void visitDeinitExistentialInst(DeinitExistentialInst *DEI) {
OS << "deinit_existential " << getIDAndType(DEI->getOperand());
}

View File

@@ -1500,6 +1500,34 @@ public:
"Could not find witness table for conformance.");
}
void checkUpcastExistentialInst(UpcastExistentialInst *UEI) {
SILType srcType = UEI->getSrcExistential().getType();
SILType destType = UEI->getDestExistential().getType();
require(srcType != destType,
"can't upcast_existential to same type");
require(srcType.isExistentialType(),
"upcast_existential source must be existential");
require(destType.isAddress(),
"upcast_existential dest must be an address");
require(destType.isExistentialType(),
"upcast_existential dest must be address of existential");
require(!destType.isClassExistentialType(),
"upcast_existential dest must be non-class existential");
}
void checkUpcastExistentialRefInst(UpcastExistentialRefInst *UEI) {
require(UEI->getOperand().getType() != UEI->getType(),
"can't upcast_existential_ref to same type");
require(UEI->getOperand().getType().isObject(),
"upcast_existential_ref operand must not be an address");
require(UEI->getOperand().getType().isClassExistentialType(),
"upcast_existential_ref operand must be class existential");
require(UEI->getType().isObject(),
"upcast_existential_ref result must not be an address");
require(UEI->getType().isClassExistentialType(),
"upcast_existential_ref result must be class existential");
}
void checkDeinitExistentialInst(DeinitExistentialInst *DEI) {
SILType exType = DEI->getOperand().getType();
require(exType.isAddress(),

View File

@@ -636,6 +636,7 @@ public:
OPERANDALIAS_MEMBEHAVIOR_INST(InjectEnumAddrInst)
OPERANDALIAS_MEMBEHAVIOR_INST(UncheckedTakeEnumDataAddrInst)
OPERANDALIAS_MEMBEHAVIOR_INST(InitExistentialInst)
OPERANDALIAS_MEMBEHAVIOR_INST(UpcastExistentialInst)
OPERANDALIAS_MEMBEHAVIOR_INST(DeinitExistentialInst)
OPERANDALIAS_MEMBEHAVIOR_INST(DeallocStackInst)
OPERANDALIAS_MEMBEHAVIOR_INST(FixLifetimeInst)

View File

@@ -94,6 +94,7 @@ static bool isTransitiveEscapeInst(SILInstruction *Inst) {
case ValueKind::DynamicMethodBranchInst:
case ValueKind::ReturnInst:
case ValueKind::AutoreleaseReturnInst:
case ValueKind::UpcastExistentialInst:
case ValueKind::FixLifetimeInst:
return false;
@@ -153,6 +154,7 @@ static bool isTransitiveEscapeInst(SILInstruction *Inst) {
case ValueKind::UnconditionalCheckedCastAddrInst:
case ValueKind::UnmanagedToRefInst:
case ValueKind::UnownedToRefInst:
case ValueKind::UpcastExistentialRefInst:
case ValueKind::UpcastInst:
return true;

View File

@@ -666,6 +666,15 @@ void ElementUseCollector::collectUses(SILValue Pointer, unsigned BaseEltNo) {
continue;
}
// upcast_existential is modeled as a load or initialization depending on
// which operand we're looking at.
if (isa<UpcastExistentialInst>(User)) {
auto Kind = UI->getOperandNumber() == 1 ?
DIUseKind::Initialization : DIUseKind::Load;
Uses.push_back(DIMemoryUse(User, Kind, BaseEltNo, 1));
continue;
}
// project_existential and open_existential are uses of the protocol value,
// so it is modeled as a load.
if (isa<ProjectExistentialInst>(User) || isa<ProtocolMethodInst>(User)

View File

@@ -256,6 +256,7 @@ static InlineCost instructionInlineCost(SILInstruction &I,
case ValueKind::RawPointerToRefInst:
case ValueKind::RefToRawPointerInst:
case ValueKind::UpcastExistentialRefInst:
case ValueKind::UpcastInst:
case ValueKind::ThinToThickFunctionInst:
@@ -364,6 +365,7 @@ static InlineCost instructionInlineCost(SILInstruction &I,
case ValueKind::UnownedReleaseInst:
case ValueKind::UnownedRetainInst:
case ValueKind::UnownedToRefInst:
case ValueKind::UpcastExistentialInst:
case ValueKind::InitBlockStorageHeaderInst:
return InlineCost::Expensive;

View File

@@ -755,6 +755,7 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB,
ONEOPERAND_ONETYPE_INST(ObjCMetatypeToObject)
ONEOPERAND_ONETYPE_INST(ObjCExistentialMetatypeToObject)
ONEOPERAND_ONETYPE_INST(ConvertFunction)
ONEOPERAND_ONETYPE_INST(UpcastExistentialRef)
ONEOPERAND_ONETYPE_INST(ProjectBlockStorage)
#undef ONEOPERAND_ONETYPE_INST
@@ -971,6 +972,18 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB,
getSILType(Ty2, (SILValueCategory)TyCategory2)));
break;
}
case ValueKind::UpcastExistentialInst: {
auto Ty = MF->getType(TyID);
auto Ty2 = MF->getType(TyID2);
bool isTake = (Attr > 0);
ResultVal = Builder.createUpcastExistential(Loc,
getLocalValue(ValID, ValResNum,
getSILType(Ty, (SILValueCategory)TyCategory)),
getLocalValue(ValID2, ValResNum2,
getSILType(Ty2, (SILValueCategory)TyCategory2)),
IsTake_t(isTake));
break;
}
case ValueKind::IntegerLiteralInst: {
auto Ty = MF->getType(TyID);
auto intTy = Ty->getAs<BuiltinIntegerType>();

View File

@@ -713,13 +713,18 @@ void SILSerializer::writeSILInstruction(const SILInstruction &SI) {
break;
}
case ValueKind::IndexAddrInst:
case ValueKind::IndexRawPointerInst: {
case ValueKind::IndexRawPointerInst:
case ValueKind::UpcastExistentialInst: {
SILValue operand, operand2;
unsigned Attr = 0;
if (SI.getKind() == ValueKind::IndexRawPointerInst) {
const IndexRawPointerInst *IRP = cast<IndexRawPointerInst>(&SI);
operand = IRP->getBase();
operand2 = IRP->getIndex();
} else if (SI.getKind() == ValueKind::UpcastExistentialInst) {
Attr = cast<UpcastExistentialInst>(&SI)->isTakeOfSrc();
operand = cast<UpcastExistentialInst>(&SI)->getSrcExistential();
operand2 = cast<UpcastExistentialInst>(&SI)->getDestExistential();
} else {
const IndexAddrInst *IAI = cast<IndexAddrInst>(&SI);
operand = IAI->getBase();
@@ -879,6 +884,7 @@ void SILSerializer::writeSILInstruction(const SILInstruction &SI) {
case ValueKind::ThickToObjCMetatypeInst:
case ValueKind::ObjCToThickMetatypeInst:
case ValueKind::ConvertFunctionInst:
case ValueKind::UpcastExistentialRefInst:
case ValueKind::ObjCMetatypeToObjectInst:
case ValueKind::ObjCExistentialMetatypeToObjectInst:
case ValueKind::ProjectBlockStorageInst: {

View File

@@ -278,6 +278,8 @@ bb0(%0 : $*Runcible, %1 : $*protocol<Bendable, Runcible>):
%4 = alloc_stack $protocol<Bendable, Runcible>
// CHECK: copy_addr {{.*}} to [initialization] {{.*}} : $*protocol<Bendable, Runcible>
%5 = copy_addr %2#1 to [initialization] %4#1 : $*protocol<Bendable, Runcible>
// CHECK: upcast_existential [take] {{.*}} : $*protocol<Bendable, Runcible> to {{.*}} : $*Runcible
%6 = upcast_existential [take] %4#1 : $*protocol<Bendable, Runcible> to %0 : $*Runcible
%7 = tuple ()
// CHECK: destroy_addr
%8 = destroy_addr %4#1 : $*protocol<Bendable, Runcible>

View File

@@ -200,7 +200,7 @@ func existentials(i: Int, dp: DerivedProtocol) {
var e : SomeProtocol // expected-note {{variable defined here}}
e.protoMe() // expected-error {{variable 'e' used before being initialized}}
var f : SomeProtocol = dp // ok, init'd by existential upcast.
var f : SomeProtocol = dp // ok, init'd by upcast_existential.
var g : DerivedProtocol // expected-note {{variable defined here}}
f = g // expected-error {{variable 'g' used before being initialized}}

View File

@@ -254,6 +254,8 @@ bb0(%0 : $*Runcible, %1 : $*protocol<Bendable, Runcible>):
%4 = alloc_stack $protocol<Bendable, Runcible>
// CHECK: copy_addr {{.*}} to [initialization] {{.*}} : $*protocol<Bendable, Runcible>
%5 = copy_addr %2#1 to [initialization] %4#1 : $*protocol<Bendable, Runcible>
// CHECK: upcast_existential [take] {{.*}} : $*protocol<Bendable, Runcible> to {{.*}} : $*Runcible
%6 = upcast_existential [take] %4#1 : $*protocol<Bendable, Runcible> to %0 : $*Runcible
%7 = tuple ()
// CHECK: destroy_addr
%8 = destroy_addr %4#1 : $*protocol<Bendable, Runcible>

View File

@@ -95,9 +95,9 @@
"enum_is_tag")
'words) . font-lock-keyword-face)
;; Protocol and Protocol Composition Types
`(,(regexp-opt '("init_existential" "deinit_existential"
`(,(regexp-opt '("init_existential" "upcast_existential" "deinit_existential"
"project_existential" "open_existential"
"init_existential_ref"
"init_existential_ref" "upcast_existential_ref"
"project_existential_ref" "open_existential_ref")
'words) . font-lock-keyword-face)
;; Unchecked Conversions

View File

@@ -32,7 +32,7 @@ syn keyword swiftKeyword apply partial_apply skipwhite
syn keyword swiftKeyword metatype value_metatype existential_metatype skipwhite
syn keyword swiftKeyword retain_value release_value tuple tuple_extract tuple_element_addr struct struct_extract struct_element_addr ref_element_addr skipwhite
syn keyword swiftKeyword init_enum_data_addr unchecked_enum_data unchecked_take_enum_data_addr inject_enum_addr skipwhite
syn keyword swiftKeyword init_existential deinit_existential project_existential open_existential init_existential_ref project_existential_ref open_existential_ref skipwhite
syn keyword swiftKeyword init_existential upcast_existential deinit_existential project_existential open_existential init_existential_ref upcast_existential_ref project_existential_ref open_existential_ref skipwhite
syn keyword swiftKeyword upcast address_to_pointer pointer_to_address unchecked_addr_cast unchecked_ref_cast ref_to_raw_pointer raw_pointer_to_ref convert_function thick_to_objc_metatype objc_to_thick_metatype thin_to_thick_function is_nonnull unchecked_ref_bit_cast unchecked_trivial_bit_cast skipwhite
syn keyword swiftKeyword unconditional_checked_cast skipwhite
syn keyword swiftKeyword cond_fail skipwhite