This patch includes a conceptually simple, but very intrusive/pervasive change.
The idea is to segregate Objective-C "object" pointers from general C pointers (utilizing the recently added ObjCObjectPointerType). The fun starts in Sema::GetTypeForDeclarator(), where "SomeInterface *" is now represented by a single AST node (rather than a PointerType whose Pointee is an ObjCInterfaceType). Since a significant amount of code assumed ObjC object pointers where based on C pointers/structs, this patch is very tedious. It should also explain why it is hard to accomplish this in smaller, self-contained patches.
This patch does most of the "heavy lifting" related to moving from PointerType->ObjCObjectPointerType. It doesn't include all potential "cleanups". The good news is additional cleanups can be done later (some are noted in the code). This patch is so large that I didn't want to include any changes that are purely aesthetic.
By making the ObjC types truly built-in, they are much easier to work with (and require fewer "hacks"). For example, there is no need for ASTContext::isObjCIdStructType() or ASTContext::isObjCClassStructType()! We believe this change (and the follow-up cleanups) will pay dividends over time.
Given the amount of code change, I do expect some fallout from this change (though it does pass all of the clang tests). If you notice any problems, please let us know asap! Thanks.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@75314 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp
index 2bd1482..dbac120 100644
--- a/lib/AST/ASTContext.cpp
+++ b/lib/AST/ASTContext.cpp
@@ -42,8 +42,8 @@
Idents(idents), Selectors(sels),
BuiltinInfo(builtins), ExternalSource(0), PrintingPolicy(LOpts) {
if (size_reserve > 0) Types.reserve(size_reserve);
- InitBuiltinTypes();
TUDecl = TranslationUnitDecl::Create(*this);
+ InitBuiltinTypes();
}
ASTContext::~ASTContext() {
@@ -190,11 +190,10 @@
LongDoubleComplexTy = getComplexType(LongDoubleTy);
BuiltinVaListType = QualType();
- ObjCIdType = QualType();
- IdStructType = 0;
- ObjCClassType = QualType();
- ClassStructType = 0;
+ ObjCIdType = QualType();
+ ObjCClassType = QualType();
+
ObjCConstantStringType = QualType();
// void * type
@@ -1071,7 +1070,7 @@
if (T->isPointerType()) {
QualType Pointee = T->getAsPointerType()->getPointeeType();
- if (Pointee->isPointerType()) {
+ if (Pointee->isPointerType() || Pointee->isObjCObjectPointerType()) {
QualType ResultType = getObjCGCQualType(Pointee, GCAttr);
return getPointerType(ResultType);
}
@@ -1847,15 +1846,18 @@
/// getObjCObjectPointerType - Return a ObjCObjectPointerType type for
/// the given interface decl and the conforming protocol list.
-QualType ASTContext::getObjCObjectPointerType(ObjCInterfaceDecl *Decl,
+QualType ASTContext::getObjCObjectPointerType(QualType InterfaceT,
ObjCProtocolDecl **Protocols,
unsigned NumProtocols) {
+ if (InterfaceT.isNull())
+ InterfaceT = QualType(ObjCObjectPointerType::getIdInterface(), 0);
+
// Sort the protocol list alphabetically to canonicalize it.
if (NumProtocols)
SortAndUniqueProtocols(Protocols, NumProtocols);
llvm::FoldingSetNodeID ID;
- ObjCObjectPointerType::Profile(ID, Decl, Protocols, NumProtocols);
+ ObjCObjectPointerType::Profile(ID, InterfaceT, Protocols, NumProtocols);
void *InsertPos = 0;
if (ObjCObjectPointerType *QT =
@@ -1864,7 +1866,7 @@
// No Match;
ObjCObjectPointerType *QType =
- new (*this,8) ObjCObjectPointerType(Decl, Protocols, NumProtocols);
+ new (*this,8) ObjCObjectPointerType(InterfaceT, Protocols, NumProtocols);
Types.push_back(QType);
ObjCObjectPointerTypes.InsertNode(QType, InsertPos);
@@ -2745,25 +2747,7 @@
S += 'j';
getObjCEncodingForTypeImpl(CT->getElementType(), S, false, false, 0, false,
false);
- } else if (T->isObjCQualifiedIdType()) {
- getObjCEncodingForTypeImpl(getObjCIdType(), S,
- ExpandPointedToStructures,
- ExpandStructures, FD);
- if (FD || EncodingProperty) {
- // Note that we do extended encoding of protocol qualifer list
- // Only when doing ivar or property encoding.
- const ObjCObjectPointerType *QIDT = T->getAsObjCQualifiedIdType();
- S += '"';
- for (ObjCObjectPointerType::qual_iterator I = QIDT->qual_begin(),
- E = QIDT->qual_end(); I != E; ++I) {
- S += '<';
- S += (*I)->getNameAsString();
- S += '>';
- }
- S += '"';
- }
- return;
- }
+ }
else if (const PointerType *PT = T->getAsPointerType()) {
QualType PointeeTy = PT->getPointeeType();
bool isReadOnly = false;
@@ -2797,42 +2781,7 @@
S.replace(S.end()-2, S.end(), replace);
}
}
- if (isObjCIdStructType(PointeeTy)) {
- S += '@';
- return;
- }
- else if (PointeeTy->isObjCInterfaceType()) {
- if (!EncodingProperty &&
- isa<TypedefType>(PointeeTy.getTypePtr())) {
- // Another historical/compatibility reason.
- // We encode the underlying type which comes out as
- // {...};
- S += '^';
- getObjCEncodingForTypeImpl(PointeeTy, S,
- false, ExpandPointedToStructures,
- NULL);
- return;
- }
- S += '@';
- if (FD || EncodingProperty) {
- const ObjCInterfaceType *OIT =
- PointeeTy.getUnqualifiedType()->getAsObjCInterfaceType();
- ObjCInterfaceDecl *OI = OIT->getDecl();
- S += '"';
- S += OI->getNameAsCString();
- for (ObjCInterfaceType::qual_iterator I = OIT->qual_begin(),
- E = OIT->qual_end(); I != E; ++I) {
- S += '<';
- S += (*I)->getNameAsString();
- S += '>';
- }
- S += '"';
- }
- return;
- } else if (isObjCClassStructType(PointeeTy)) {
- S += '#';
- return;
- } else if (isObjCSelType(PointeeTy)) {
+ if (isObjCSelType(PointeeTy)) {
S += ':';
return;
}
@@ -2937,7 +2886,61 @@
}
S += '}';
}
- else
+ else if (const ObjCObjectPointerType *OPT = T->getAsObjCObjectPointerType()) {
+ if (OPT->isObjCIdType()) {
+ S += '@';
+ return;
+ } else if (OPT->isObjCClassType()) {
+ S += '#';
+ return;
+ } else if (OPT->isObjCQualifiedIdType()) {
+ getObjCEncodingForTypeImpl(getObjCIdType(), S,
+ ExpandPointedToStructures,
+ ExpandStructures, FD);
+ if (FD || EncodingProperty) {
+ // Note that we do extended encoding of protocol qualifer list
+ // Only when doing ivar or property encoding.
+ const ObjCObjectPointerType *QIDT = T->getAsObjCQualifiedIdType();
+ S += '"';
+ for (ObjCObjectPointerType::qual_iterator I = QIDT->qual_begin(),
+ E = QIDT->qual_end(); I != E; ++I) {
+ S += '<';
+ S += (*I)->getNameAsString();
+ S += '>';
+ }
+ S += '"';
+ }
+ return;
+ } else {
+ QualType PointeeTy = OPT->getPointeeType();
+ if (!EncodingProperty &&
+ isa<TypedefType>(PointeeTy.getTypePtr())) {
+ // Another historical/compatibility reason.
+ // We encode the underlying type which comes out as
+ // {...};
+ S += '^';
+ getObjCEncodingForTypeImpl(PointeeTy, S,
+ false, ExpandPointedToStructures,
+ NULL);
+ return;
+ }
+ S += '@';
+ if (FD || EncodingProperty) {
+ const ObjCInterfaceType *OIT = OPT->getInterfaceType();
+ ObjCInterfaceDecl *OI = OIT->getDecl();
+ S += '"';
+ S += OI->getNameAsCString();
+ for (ObjCInterfaceType::qual_iterator I = OIT->qual_begin(),
+ E = OIT->qual_end(); I != E; ++I) {
+ S += '<';
+ S += (*I)->getNameAsString();
+ S += '>';
+ }
+ S += '"';
+ }
+ return;
+ }
+ } else
assert(0 && "@encode for type not implemented!");
}
@@ -2967,23 +2970,12 @@
void ASTContext::setObjCIdType(QualType T)
{
ObjCIdType = T;
-
const TypedefType *TT = T->getAsTypedefType();
- if (!TT)
- return;
-
- TypedefDecl *TD = TT->getDecl();
-
- // typedef struct objc_object *id;
- const PointerType *ptr = TD->getUnderlyingType()->getAsPointerType();
- // User error - caller will issue diagnostics.
- if (!ptr)
- return;
- const RecordType *rec = ptr->getPointeeType()->getAsStructureType();
- // User error - caller will issue diagnostics.
- if (!rec)
- return;
- IdStructType = rec;
+ assert(TT && "missing 'id' typedef");
+ const ObjCObjectPointerType *OPT =
+ TT->getDecl()->getUnderlyingType()->getAsObjCObjectPointerType();
+ assert(OPT && "missing 'id' type");
+ ObjCObjectPointerType::setIdInterface(OPT->getPointeeType());
}
void ASTContext::setObjCSelType(QualType T)
@@ -3013,18 +3005,12 @@
void ASTContext::setObjCClassType(QualType T)
{
ObjCClassType = T;
-
const TypedefType *TT = T->getAsTypedefType();
- if (!TT)
- return;
- TypedefDecl *TD = TT->getDecl();
-
- // typedef struct objc_class *Class;
- const PointerType *ptr = TD->getUnderlyingType()->getAsPointerType();
- assert(ptr && "'Class' incorrectly typed");
- const RecordType *rec = ptr->getPointeeType()->getAsStructureType();
- assert(rec && "'Class' incorrectly typed");
- ClassStructType = rec;
+ assert(TT && "missing 'Class' typedef");
+ const ObjCObjectPointerType *OPT =
+ TT->getDecl()->getUnderlyingType()->getAsObjCObjectPointerType();
+ assert(OPT && "missing 'Class' type");
+ ObjCObjectPointerType::setClassInterface(OPT->getPointeeType());
}
void ASTContext::setObjCConstantStringInterface(ObjCInterfaceDecl *Decl) {
@@ -3123,6 +3109,8 @@
/// to struct), Interface* (pointer to ObjCInterfaceType) and id<P> (qualified
/// ID type).
bool ASTContext::isObjCObjectPointerType(QualType Ty) const {
+ if (Ty->isObjCObjectPointerType())
+ return true;
if (Ty->isObjCQualifiedIdType())
return true;
@@ -3198,8 +3186,30 @@
/// compatible for assignment from RHS to LHS. This handles validation of any
/// protocol qualifiers on the LHS or RHS.
///
+/// FIXME: Move the following to ObjCObjectPointerType/ObjCInterfaceType.
+bool ASTContext::canAssignObjCInterfaces(const ObjCObjectPointerType *LHSOPT,
+ const ObjCObjectPointerType *RHSOPT) {
+ // If either interface represents the built-in 'id' or 'Class' types,
+ // then return true (no need to call canAssignObjCInterfaces()).
+ if (LHSOPT->isObjCIdType() || RHSOPT->isObjCIdType() ||
+ LHSOPT->isObjCClassType() || RHSOPT->isObjCClassType())
+ return true;
+
+ const ObjCInterfaceType* LHS = LHSOPT->getInterfaceType();
+ const ObjCInterfaceType* RHS = RHSOPT->getInterfaceType();
+ if (!LHS || !RHS)
+ return false;
+ return canAssignObjCInterfaces(LHS, RHS);
+}
+
bool ASTContext::canAssignObjCInterfaces(const ObjCInterfaceType *LHS,
const ObjCInterfaceType *RHS) {
+ // If either interface represents the built-in 'id' or 'Class' types,
+ // then return true.
+ if (LHS->isObjCIdInterface() || RHS->isObjCIdInterface() ||
+ LHS->isObjCClassInterface() || RHS->isObjCClassInterface())
+ return true;
+
// Verify that the base decls are compatible: the RHS must be a subclass of
// the LHS.
if (!LHS->getDecl()->isSuperClassOf(RHS->getDecl()))
@@ -3245,25 +3255,14 @@
bool ASTContext::areComparableObjCPointerTypes(QualType LHS, QualType RHS) {
// get the "pointed to" types
- const PointerType *LHSPT = LHS->getAsPointerType();
- const PointerType *RHSPT = RHS->getAsPointerType();
+ const ObjCObjectPointerType *LHSOPT = LHS->getAsObjCObjectPointerType();
+ const ObjCObjectPointerType *RHSOPT = RHS->getAsObjCObjectPointerType();
- if (!LHSPT || !RHSPT)
+ if (!LHSOPT || !RHSOPT)
return false;
-
- QualType lhptee = LHSPT->getPointeeType();
- QualType rhptee = RHSPT->getPointeeType();
- const ObjCInterfaceType* LHSIface = lhptee->getAsObjCInterfaceType();
- const ObjCInterfaceType* RHSIface = rhptee->getAsObjCInterfaceType();
- // ID acts sort of like void* for ObjC interfaces
- if (LHSIface && isObjCIdStructType(rhptee))
- return true;
- if (RHSIface && isObjCIdStructType(lhptee))
- return true;
- if (!LHSIface || !RHSIface)
- return false;
- return canAssignObjCInterfaces(LHSIface, RHSIface) ||
- canAssignObjCInterfaces(RHSIface, LHSIface);
+
+ return canAssignObjCInterfaces(LHSOPT, RHSOPT) ||
+ canAssignObjCInterfaces(RHSOPT, LHSOPT);
}
/// typesAreCompatible - C99 6.7.3p9: For two qualified types to be compatible,
@@ -3406,8 +3405,7 @@
// issue error.
if ((GCAttr == QualType::Weak && GCLHSAttr != GCAttr) ||
(GCAttr == QualType::Strong && GCLHSAttr != GCAttr &&
- LHSCan->isPointerType() && !isObjCObjectPointerType(LHSCan) &&
- !isObjCIdStructType(LHSCan->getAsPointerType()->getPointeeType())))
+ !LHSCan->isObjCObjectPointerType()))
return QualType();
RHS = QualType(cast<ExtQualType>(RHS.getDesugaredType())->getBaseType(),
@@ -3432,8 +3430,7 @@
// issue error.
if ((GCAttr == QualType::Weak && GCRHSAttr != GCAttr) ||
(GCAttr == QualType::Strong && GCRHSAttr != GCAttr &&
- RHSCan->isPointerType() && !isObjCObjectPointerType(RHSCan) &&
- !isObjCIdStructType(RHSCan->getAsPointerType()->getPointeeType())))
+ !RHSCan->isObjCObjectPointerType()))
return QualType();
LHS = QualType(cast<ExtQualType>(LHS.getDesugaredType())->getBaseType(),
@@ -3460,47 +3457,12 @@
if (RHSClass == Type::ExtVector) RHSClass = Type::Vector;
// Consider qualified interfaces and interfaces the same.
+ // FIXME: Remove (ObjCObjectPointerType should obsolete this funny business).
if (LHSClass == Type::ObjCQualifiedInterface) LHSClass = Type::ObjCInterface;
if (RHSClass == Type::ObjCQualifiedInterface) RHSClass = Type::ObjCInterface;
// If the canonical type classes don't match.
if (LHSClass != RHSClass) {
- const ObjCInterfaceType* LHSIface = LHS->getAsObjCInterfaceType();
- const ObjCInterfaceType* RHSIface = RHS->getAsObjCInterfaceType();
-
- // 'id' and 'Class' act sort of like void* for ObjC interfaces
- if (LHSIface && (isObjCIdStructType(RHS) || isObjCClassStructType(RHS)))
- return LHS;
- if (RHSIface && (isObjCIdStructType(LHS) || isObjCClassStructType(LHS)))
- return RHS;
-
- // ID is compatible with all qualified id types.
- if (LHS->isObjCQualifiedIdType()) {
- if (const PointerType *PT = RHS->getAsPointerType()) {
- QualType pType = PT->getPointeeType();
- if (isObjCIdStructType(pType) || isObjCClassStructType(pType))
- return LHS;
- // FIXME: need to use ObjCQualifiedIdTypesAreCompatible(LHS, RHS, true).
- // Unfortunately, this API is part of Sema (which we don't have access
- // to. Need to refactor. The following check is insufficient, since we
- // need to make sure the class implements the protocol.
- if (pType->isObjCInterfaceType())
- return LHS;
- }
- }
- if (RHS->isObjCQualifiedIdType()) {
- if (const PointerType *PT = LHS->getAsPointerType()) {
- QualType pType = PT->getPointeeType();
- if (isObjCIdStructType(pType) || isObjCClassStructType(pType))
- return RHS;
- // FIXME: need to use ObjCQualifiedIdTypesAreCompatible(LHS, RHS, true).
- // Unfortunately, this API is part of Sema (which we don't have access
- // to. Need to refactor. The following check is insufficient, since we
- // need to make sure the class implements the protocol.
- if (pType->isObjCInterfaceType())
- return RHS;
- }
- }
// C99 6.7.2.2p4: Each enumerated type shall be compatible with char,
// a signed integer type, or an unsigned integer type.
if (const EnumType* ETy = LHS->getAsEnumType()) {
@@ -3611,9 +3573,6 @@
return mergeFunctionTypes(LHS, RHS);
case Type::Record:
case Type::Enum:
- // FIXME: Why are these compatible?
- if (isObjCIdStructType(LHS) && isObjCClassStructType(RHS)) return LHS;
- if (isObjCClassStructType(LHS) && isObjCIdStructType(RHS)) return LHS;
return QualType();
case Type::Builtin:
// Only exactly equal builtin types are compatible, which is tested above.
@@ -3638,10 +3597,17 @@
return QualType();
}
- case Type::ObjCObjectPointer:
- // FIXME: finish
- // Distinct qualified id's are not compatible.
+ case Type::ObjCObjectPointer: {
+ // FIXME: Incorporate tests from Sema::ObjCQualifiedIdTypesAreCompatible().
+ if (LHS->isObjCQualifiedIdType() && RHS->isObjCQualifiedIdType())
+ return QualType();
+
+ if (canAssignObjCInterfaces(LHS->getAsObjCObjectPointerType(),
+ RHS->getAsObjCObjectPointerType()))
+ return LHS;
+
return QualType();
+ }
case Type::FixedWidthInt:
// Distinct fixed-width integers are not compatible.
return QualType();
diff --git a/lib/AST/DeclObjC.cpp b/lib/AST/DeclObjC.cpp
index 54f13e1..613dc58 100644
--- a/lib/AST/DeclObjC.cpp
+++ b/lib/AST/DeclObjC.cpp
@@ -280,7 +280,7 @@
// of the interface (which has been reported). Recover gracefully.
if (OID) {
selfTy = Context.getObjCInterfaceType(OID);
- selfTy = Context.getPointerType(selfTy);
+ selfTy = Context.getObjCObjectPointerType(selfTy);
} else {
selfTy = Context.getObjCIdType();
}
diff --git a/lib/AST/Expr.cpp b/lib/AST/Expr.cpp
index 970e8bc..2c65179 100644
--- a/lib/AST/Expr.cpp
+++ b/lib/AST/Expr.cpp
@@ -1095,6 +1095,7 @@
switch (getStmtClass()) {
default: break;
case StringLiteralClass:
+ case ObjCStringLiteralClass:
case ObjCEncodeExprClass:
return true;
case CompoundLiteralExprClass: {
@@ -1136,7 +1137,6 @@
return cast<CastExpr>(this)->getSubExpr()->isConstantInitializer(Ctx);
break;
}
-
return isEvaluatable(Ctx);
}
diff --git a/lib/AST/ExprConstant.cpp b/lib/AST/ExprConstant.cpp
index eb6b5b7..c3d0402 100644
--- a/lib/AST/ExprConstant.cpp
+++ b/lib/AST/ExprConstant.cpp
@@ -382,7 +382,8 @@
const Expr* SubExpr = E->getSubExpr();
// Check for pointer->pointer cast
- if (SubExpr->getType()->isPointerType()) {
+ if (SubExpr->getType()->isPointerType() ||
+ SubExpr->getType()->isObjCObjectPointerType()) {
APValue Result;
if (EvaluatePointer(SubExpr, Result, Info))
return Result;
diff --git a/lib/AST/Type.cpp b/lib/AST/Type.cpp
index 78c3b78..fd368c4 100644
--- a/lib/AST/Type.cpp
+++ b/lib/AST/Type.cpp
@@ -22,6 +22,9 @@
#include "llvm/Support/raw_ostream.h"
using namespace clang;
+ObjCInterfaceType *ObjCObjectPointerType::IdInterfaceT;
+ObjCInterfaceType *ObjCObjectPointerType::ClassInterfaceT;
+
bool QualType::isConstant(ASTContext &Ctx) const {
if (isConstQualified())
return true;
@@ -295,6 +298,15 @@
return dyn_cast_or_null<FunctionProtoType>(getAsFunctionType());
}
+QualType Type::getPointeeType() const {
+ if (const PointerType *PT = getAsPointerType())
+ return PT->getPointeeType();
+ if (const ObjCObjectPointerType *OPT = getAsObjCObjectPointerType())
+ return OPT->getPointeeType();
+ if (const BlockPointerType *BPT = getAsBlockPointerType())
+ return BPT->getPointeeType();
+ return QualType();
+}
const PointerType *Type::getAsPointerType() const {
// If this is directly a pointer type, return it.
@@ -609,6 +621,14 @@
return 0;
}
+const ObjCObjectPointerType *Type::getAsObjCInterfacePointerType() const {
+ if (const ObjCObjectPointerType *OPT = getAsObjCObjectPointerType()) {
+ if (OPT->getInterfaceType())
+ return OPT;
+ }
+ return 0;
+}
+
const TemplateTypeParmType *Type::getAsTemplateTypeParmType() const {
// There is no sugar for template type parameters, so just return
// the canonical type pointer if it is the right class.
@@ -1016,16 +1036,18 @@
}
void ObjCObjectPointerType::Profile(llvm::FoldingSetNodeID &ID,
- const ObjCInterfaceDecl *Decl,
- ObjCProtocolDecl **protocols,
+ QualType OIT, ObjCProtocolDecl **protocols,
unsigned NumProtocols) {
- ID.AddPointer(Decl);
+ ID.AddPointer(OIT.getAsOpaquePtr());
for (unsigned i = 0; i != NumProtocols; i++)
ID.AddPointer(protocols[i]);
}
void ObjCObjectPointerType::Profile(llvm::FoldingSetNodeID &ID) {
- Profile(ID, getDecl(), &Protocols[0], getNumProtocols());
+ if (getNumProtocols())
+ Profile(ID, getPointeeType(), &Protocols[0], getNumProtocols());
+ else
+ Profile(ID, getPointeeType(), 0, 0);
}
void ObjCQualifiedInterfaceType::Profile(llvm::FoldingSetNodeID &ID,
@@ -1663,6 +1685,14 @@
InnerString = MyString + ' ' + InnerString;
}
+bool ObjCInterfaceType::isObjCIdInterface() const {
+ return this == ObjCObjectPointerType::getIdInterface();
+}
+
+bool ObjCInterfaceType::isObjCClassInterface() const {
+ return this == ObjCObjectPointerType::getClassInterface();
+}
+
void ObjCInterfaceType::getAsStringInternal(std::string &InnerString, const PrintingPolicy &Policy) const {
if (!InnerString.empty()) // Prefix the basic type, e.g. 'typedefname X'.
InnerString = ' ' + InnerString;
@@ -1671,15 +1701,7 @@
void ObjCObjectPointerType::getAsStringInternal(std::string &InnerString,
const PrintingPolicy &Policy) const {
- if (!InnerString.empty()) // Prefix the basic type, e.g. 'typedefname X'.
- InnerString = ' ' + InnerString;
-
- std::string ObjCQIString;
-
- if (getDecl())
- ObjCQIString = getDecl()->getNameAsString();
- else
- ObjCQIString = "id";
+ std::string ObjCQIString = getInterfaceType()->getDecl()->getNameAsString();
if (!qual_empty()) {
ObjCQIString += '<';
@@ -1690,6 +1712,11 @@
}
ObjCQIString += '>';
}
+ if (!isObjCIdType() && !isObjCQualifiedIdType())
+ ObjCQIString += " *"; // Don't forget the implicit pointer.
+ else if (!InnerString.empty()) // Prefix the basic type, e.g. 'typedefname X'.
+ InnerString = ' ' + InnerString;
+
InnerString = ObjCQIString + InnerString;
}