]> granicus.if.org Git - clang/commitdiff
Start implementing support for substitution into pack expansions that
authorDouglas Gregor <dgregor@apple.com>
Fri, 14 Jan 2011 02:55:32 +0000 (02:55 +0000)
committerDouglas Gregor <dgregor@apple.com>
Fri, 14 Jan 2011 02:55:32 +0000 (02:55 +0000)
involve template parameter packs at multiple template levels that
occur within the signatures members of class templates (and partial
specializations thereof). This is a work-in-progress that is deficient
in several ways, notably:
  - It only works for template type parameter packs, but we need to
  also support non-type template parameter packs and template template
  parameter packs.
  - It doesn't keep track of the lengths of the substituted argument
  packs in the expansion, so it can't properly diagnose length
  mismatches.

However, this is a concrete step in the right direction.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@123425 91177308-0d34-0410-b5e6-96231b3b80d8

18 files changed:
include/clang/AST/ASTContext.h
include/clang/AST/RecursiveASTVisitor.h
include/clang/AST/Type.h
include/clang/AST/TypeLoc.h
include/clang/AST/TypeNodes.def
include/clang/Serialization/ASTBitCodes.h
lib/AST/ASTContext.cpp
lib/AST/ItaniumMangle.cpp
lib/AST/MicrosoftMangle.cpp
lib/AST/Type.cpp
lib/AST/TypePrinter.cpp
lib/Sema/SemaTemplate.cpp
lib/Sema/SemaTemplateInstantiate.cpp
lib/Sema/SemaTemplateVariadic.cpp
lib/Sema/TreeTransform.h
lib/Serialization/ASTReader.cpp
lib/Serialization/ASTWriter.cpp
test/CXX/temp/temp.decls/temp.variadic/multi-level-substitution.cpp

index a0544ef8bdd4dae1f05e236ea57e7bf9bb0e935e..15bc301bd923b0a0acb8420847f4c62777f713b8 100644 (file)
@@ -102,6 +102,8 @@ class ASTContext {
   mutable llvm::FoldingSet<TemplateTypeParmType> TemplateTypeParmTypes;
   mutable llvm::FoldingSet<SubstTemplateTypeParmType>
     SubstTemplateTypeParmTypes;
+  mutable llvm::FoldingSet<SubstTemplateTypeParmPackType>
+    SubstTemplateTypeParmPackTypes;
   mutable llvm::ContextualFoldingSet<TemplateSpecializationType, ASTContext&>
     TemplateSpecializationTypes;
   mutable llvm::FoldingSet<ParenType> ParenTypes;
@@ -664,6 +666,9 @@ public:
 
   QualType getSubstTemplateTypeParmType(const TemplateTypeParmType *Replaced,
                                         QualType Replacement) const;
+  QualType getSubstTemplateTypeParmPackType(
+                                          const TemplateTypeParmType *Replaced,
+                                            const TemplateArgument &ArgPack);
 
   QualType getTemplateTypeParmType(unsigned Depth, unsigned Index,
                                    bool ParameterPack,
index 31bb50dd9abf954d1b53903fca8094823bee14d7..3d1279b2527c0f099c9a77c257235b05b6e535fc 100644 (file)
@@ -718,6 +718,7 @@ DEF_TRAVERSE_TYPE(RecordType, { })
 DEF_TRAVERSE_TYPE(EnumType, { })
 DEF_TRAVERSE_TYPE(TemplateTypeParmType, { })
 DEF_TRAVERSE_TYPE(SubstTemplateTypeParmType, { })
+DEF_TRAVERSE_TYPE(SubstTemplateTypeParmPackType, { })
 
 DEF_TRAVERSE_TYPE(TemplateSpecializationType, {
     TRY_TO(TraverseTemplateName(T->getTemplateName()));
@@ -925,6 +926,7 @@ DEF_TRAVERSE_TYPELOC(RecordType, { })
 DEF_TRAVERSE_TYPELOC(EnumType, { })
 DEF_TRAVERSE_TYPELOC(TemplateTypeParmType, { })
 DEF_TRAVERSE_TYPELOC(SubstTemplateTypeParmType, { })
+DEF_TRAVERSE_TYPELOC(SubstTemplateTypeParmPackType, { })
 
 // FIXME: use the loc for the template name?
 DEF_TRAVERSE_TYPELOC(TemplateSpecializationType, {
index 19a16a343731f0ce593d8e55e5e6ceccfa851cab..c081a161de535cde00167dc57bcbf5652d8b77f7 100644 (file)
@@ -2860,6 +2860,59 @@ public:
   static bool classof(const SubstTemplateTypeParmType *T) { return true; }
 };
 
+/// \brief Represents the result of substituting a set of types for a template
+/// type parameter pack.
+///
+/// When a pack expansion in the source code contains multiple parameter packs
+/// and those parameter packs correspond to different levels of template
+/// parameter lists, this type node is used to represent a template type 
+/// parameter pack from an outer level, which has already had its argument pack
+/// substituted but that still lives within a pack expansion that itself
+/// could not be instantiated. When actually performing a substitution into
+/// that pack expansion (e.g., when all template parameters have corresponding
+/// arguments), this type will be replaced with the \c SubstTemplateTypeParmType
+/// at the current pack substitution index.
+class SubstTemplateTypeParmPackType : public Type, public llvm::FoldingSetNode {
+  /// \brief The original type parameter.
+  const TemplateTypeParmType *Replaced;
+  
+  /// \brief A pointer to the set of template arguments that this
+  /// parameter pack is instantiated with.
+  const TemplateArgument *Arguments;
+  
+  /// \brief The number of template arguments in \c Arguments.
+  unsigned NumArguments;
+  
+  SubstTemplateTypeParmPackType(const TemplateTypeParmType *Param, 
+                                QualType Canon,
+                                const TemplateArgument &ArgPack);
+  
+  friend class ASTContext;
+  
+public:
+  IdentifierInfo *getName() const { return Replaced->getName(); }
+  
+  /// Gets the template parameter that was substituted for.
+  const TemplateTypeParmType *getReplacedParameter() const {
+    return Replaced;
+  }
+  
+  bool isSugared() const { return false; }
+  QualType desugar() const { return QualType(this, 0); }
+  
+  TemplateArgument getArgumentPack() const;
+  
+  void Profile(llvm::FoldingSetNodeID &ID);
+  static void Profile(llvm::FoldingSetNodeID &ID,
+                      const TemplateTypeParmType *Replaced,
+                      const TemplateArgument &ArgPack);
+  
+  static bool classof(const Type *T) {
+    return T->getTypeClass() == SubstTemplateTypeParmPack;
+  }
+  static bool classof(const SubstTemplateTypeParmPackType *T) { return true; }
+};
+
 /// \brief Represents the type of a template specialization as written
 /// in the source code.
 ///
index 16a8cb40c83fad4982a4c27a335c72413c632d48..0b8ec0412ac72f2a4658269cbd4326aa94ee0518 100644 (file)
@@ -592,6 +592,13 @@ class SubstTemplateTypeParmTypeLoc :
                                      SubstTemplateTypeParmType> {
 };
 
+  /// \brief Wrapper for substituted template type parameters.
+class SubstTemplateTypeParmPackTypeLoc :
+    public InheritingConcreteTypeLoc<TypeSpecTypeLoc,
+                                     SubstTemplateTypeParmPackTypeLoc,
+                                     SubstTemplateTypeParmPackType> {
+};
+
 struct AttributedLocInfo {
   union {
     Expr *ExprOperand;
index 52b8f831ac449743489a3ca82a7355246c11cb73..3587767f8fd8d61ac0c3a30d083397afc467e2be 100644 (file)
@@ -91,6 +91,7 @@ NON_CANONICAL_TYPE(Elaborated, Type)
 NON_CANONICAL_TYPE(Attributed, Type)
 DEPENDENT_TYPE(TemplateTypeParm, Type)
 NON_CANONICAL_TYPE(SubstTemplateTypeParm, Type)
+DEPENDENT_TYPE(SubstTemplateTypeParmPack, Type)
 NON_CANONICAL_UNLESS_DEPENDENT_TYPE(TemplateSpecialization, Type)
 DEPENDENT_TYPE(InjectedClassName, Type)
 DEPENDENT_TYPE(DependentName, Type)
index ea93d2e1d6c03ba33d17bf8af9c8f73f6c678f64..0ea91329154c16cb187f7ee560e1439ff589a894 100644 (file)
@@ -555,7 +555,9 @@ namespace clang {
       /// \brief A PackExpansionType record.
       TYPE_PACK_EXPANSION           = 35,
       /// \brief An AttributedType record.
-      TYPE_ATTRIBUTED               = 36
+      TYPE_ATTRIBUTED               = 36,
+      /// \brief A SubstTemplateTypeParmPackType record.
+      TYPE_SUBST_TEMPLATE_TYPE_PARM_PACK = 37
     };
 
     /// \brief The type IDs for special types constructed by semantic
index 3abfe22caa89c47927eb4e9bb084a7f11d89eedb..35d4b7a097392d65c34b816bbf9f146a2f9769d1 100644 (file)
@@ -1932,6 +1932,42 @@ ASTContext::getSubstTemplateTypeParmType(const TemplateTypeParmType *Parm,
   return QualType(SubstParm, 0);
 }
 
+/// \brief Retrieve a 
+QualType ASTContext::getSubstTemplateTypeParmPackType(
+                                          const TemplateTypeParmType *Parm,
+                                              const TemplateArgument &ArgPack) {
+#ifndef NDEBUG
+  for (TemplateArgument::pack_iterator P = ArgPack.pack_begin(), 
+                                    PEnd = ArgPack.pack_end();
+       P != PEnd; ++P) {
+    assert(P->getKind() == TemplateArgument::Type &&"Pack contains a non-type");
+    assert(P->getAsType().isCanonical() && "Pack contains non-canonical type");
+  }
+#endif
+  
+  llvm::FoldingSetNodeID ID;
+  SubstTemplateTypeParmPackType::Profile(ID, Parm, ArgPack);
+  void *InsertPos = 0;
+  if (SubstTemplateTypeParmPackType *SubstParm
+        = SubstTemplateTypeParmPackTypes.FindNodeOrInsertPos(ID, InsertPos))
+    return QualType(SubstParm, 0);
+  
+  QualType Canon;
+  if (!Parm->isCanonicalUnqualified()) {
+    Canon = getCanonicalType(QualType(Parm, 0));
+    Canon = getSubstTemplateTypeParmPackType(cast<TemplateTypeParmType>(Canon),
+                                             ArgPack);
+    SubstTemplateTypeParmPackTypes.FindNodeOrInsertPos(ID, InsertPos);
+  }
+
+  SubstTemplateTypeParmPackType *SubstParm
+    = new (*this, TypeAlignment) SubstTemplateTypeParmPackType(Parm, Canon,
+                                                               ArgPack);
+  Types.push_back(SubstParm);
+  SubstTemplateTypeParmTypes.InsertNode(SubstParm, InsertPos);
+  return QualType(SubstParm, 0);  
+}
+
 /// \brief Retrieve the template type parameter type for a template
 /// parameter or parameter pack with the given depth, index, and (optionally)
 /// name.
index 8daba88ea863df10da18b30fd55abe1aba361546..0ebd7da679c2c30142dec0e640637953a3cf7546 100644 (file)
@@ -1383,6 +1383,11 @@ void CXXNameMangler::mangleType(const TemplateTypeParmType *T) {
   mangleTemplateParameter(T->getIndex());
 }
 
+// <type>           ::= <template-param>
+void CXXNameMangler::mangleType(const SubstTemplateTypeParmPackType *T) {
+  mangleTemplateParameter(T->getReplacedParameter()->getIndex());
+}
+
 // <type> ::= P <type>   # pointer-to
 void CXXNameMangler::mangleType(const PointerType *T) {
   Out << 'P';
index ef50899e439c2163304abcb5198710c70f298788..ce53fc21746d2bfe9c81c49885954d1cf517386d 100644 (file)
@@ -1011,6 +1011,12 @@ void MicrosoftCXXNameMangler::mangleType(const TemplateTypeParmType *T) {
   assert(false && "Don't know how to mangle TemplateTypeParmTypes yet!");
 }
 
+void MicrosoftCXXNameMangler::mangleType(
+                                       const SubstTemplateTypeParmPackType *T) {
+  assert(false && 
+         "Don't know how to mangle SubstTemplateTypeParmPackTypes yet!");
+}
+
 // <type> ::= <pointer-type>
 // <pointer-type> ::= <pointer-cvr-qualifiers> <cvr-qualifiers> <type>
 void MicrosoftCXXNameMangler::mangleType(const PointerType *T) {
index d96993e7bd9d1e1f13f82731016455f9581ee07b..3882f4960acc95ed67f3c34ec25fe50d47e6340f 100644 (file)
@@ -1244,6 +1244,34 @@ bool EnumType::classof(const TagType *TT) {
   return isa<EnumDecl>(TT->getDecl());
 }
 
+SubstTemplateTypeParmPackType::
+SubstTemplateTypeParmPackType(const TemplateTypeParmType *Param, 
+                              QualType Canon,
+                              const TemplateArgument &ArgPack)
+  : Type(SubstTemplateTypeParmPack, Canon, true, false, true), Replaced(Param), 
+    Arguments(ArgPack.pack_begin()), NumArguments(ArgPack.pack_size()) 
+{ 
+}
+
+TemplateArgument SubstTemplateTypeParmPackType::getArgumentPack() const {
+  return TemplateArgument(Arguments, NumArguments);
+}
+
+void SubstTemplateTypeParmPackType::Profile(llvm::FoldingSetNodeID &ID) {
+  Profile(ID, getReplacedParameter(), getArgumentPack());
+}
+
+void SubstTemplateTypeParmPackType::Profile(llvm::FoldingSetNodeID &ID,
+                                           const TemplateTypeParmType *Replaced,
+                                            const TemplateArgument &ArgPack) {
+  ID.AddPointer(Replaced);
+  ID.AddInteger(ArgPack.pack_size());
+  for (TemplateArgument::pack_iterator P = ArgPack.pack_begin(), 
+                                    PEnd = ArgPack.pack_end();
+       P != PEnd; ++P)
+    ID.AddPointer(P->getAsType().getAsOpaquePtr());
+}
+
 bool TemplateSpecializationType::
 anyDependentTemplateArguments(const TemplateArgumentListInfo &Args) {
   return anyDependentTemplateArguments(Args.getArgumentArray(), Args.size());
index ef9704ec8581795f67bd7c8cd564e81bc287edbf..8a740d4ad8a6bd4faf0f1069ff1731a0c4a7769d 100644 (file)
@@ -568,6 +568,12 @@ void TypePrinter::printSubstTemplateTypeParm(const SubstTemplateTypeParmType *T,
   print(T->getReplacementType(), S);
 }
 
+void TypePrinter::printSubstTemplateTypeParmPack(
+                                        const SubstTemplateTypeParmPackType *T, 
+                                             std::string &S) { 
+  printTemplateTypeParm(T->getReplacedParameter(), S);
+}
+
 void TypePrinter::printTemplateSpecialization(
                                             const TemplateSpecializationType *T, 
                                               std::string &S) { 
index 868458c2a98ce5f5b5118158abee0cb07895bcbe..6a37482d66a3c942ab12f6d797526364379ae337 100644 (file)
@@ -2713,6 +2713,11 @@ bool UnnamedLocalNoLinkageFinder::VisitTemplateTypeParmType(
   return false;
 }
 
+bool UnnamedLocalNoLinkageFinder::VisitSubstTemplateTypeParmPackType(
+                                        const SubstTemplateTypeParmPackType *) {
+  return false;
+}
+
 bool UnnamedLocalNoLinkageFinder::VisitTemplateSpecializationType(
                                             const TemplateSpecializationType*) {
   return false;
index 5f181fb5e1cedf64640b00ff8a42b56880d9924a..629dc589588c742052e41bdaa3a303cb3888f849 100644 (file)
@@ -712,6 +712,12 @@ namespace {
     QualType TransformTemplateTypeParmType(TypeLocBuilder &TLB,
                                            TemplateTypeParmTypeLoc TL);
 
+    /// \brief Transforms an already-substituted template type parameter pack
+    /// into either itself (if we aren't substituting into its pack expansion)
+    /// or the appropriate substituted argument.
+    QualType TransformSubstTemplateTypeParmPackType(TypeLocBuilder &TLB,
+                                           SubstTemplateTypeParmPackTypeLoc TL);
+
     ExprResult TransformCallExpr(CallExpr *CE) {
       getSema().CallsUndergoingInstantiation.push_back(CE);
       ExprResult Result =
@@ -1024,10 +1030,15 @@ TemplateInstantiator::TransformTemplateTypeParmType(TypeLocBuilder &TLB,
              "Missing argument pack");
       
       if (getSema().ArgumentPackSubstitutionIndex == -1) {
-        // FIXME: Variadic templates fun case.
-        getSema().Diag(TL.getSourceRange().getBegin(), 
-                       diag::err_pack_expansion_mismatch_unsupported);
-        return QualType();
+        // We have the template argument pack, but we're not expanding the
+        // enclosing pack expansion yet. Just save the template argument
+        // pack for later substitution.
+        QualType Result
+          = getSema().Context.getSubstTemplateTypeParmPackType(T, Arg);
+        SubstTemplateTypeParmPackTypeLoc NewTL
+          = TLB.push<SubstTemplateTypeParmPackTypeLoc>(Result);
+        NewTL.setNameLoc(TL.getNameLoc());
+        return Result;
       }
       
       assert(getSema().ArgumentPackSubstitutionIndex < (int)Arg.pack_size());
@@ -1063,6 +1074,32 @@ TemplateInstantiator::TransformTemplateTypeParmType(TypeLocBuilder &TLB,
   return Result;
 }
 
+QualType 
+TemplateInstantiator::TransformSubstTemplateTypeParmPackType(
+                                                            TypeLocBuilder &TLB,
+                                         SubstTemplateTypeParmPackTypeLoc TL) {
+  if (getSema().ArgumentPackSubstitutionIndex == -1) {
+    // We aren't expanding the parameter pack, so just return ourselves.
+    SubstTemplateTypeParmPackTypeLoc NewTL
+      = TLB.push<SubstTemplateTypeParmPackTypeLoc>(TL.getType());
+    NewTL.setNameLoc(TL.getNameLoc());
+    return TL.getType();
+  }
+  
+  const TemplateArgument &ArgPack = TL.getTypePtr()->getArgumentPack();
+  unsigned Index = (unsigned)getSema().ArgumentPackSubstitutionIndex;
+  assert(Index < ArgPack.pack_size() && "Substitution index out-of-range");
+  
+  QualType Result = ArgPack.pack_begin()[Index].getAsType();
+  Result = getSema().Context.getSubstTemplateTypeParmType(
+                                      TL.getTypePtr()->getReplacedParameter(),
+                                                          Result);
+  SubstTemplateTypeParmTypeLoc NewTL
+    = TLB.push<SubstTemplateTypeParmTypeLoc>(Result);
+  NewTL.setNameLoc(TL.getNameLoc());
+  return Result;
+}
+
 /// \brief Perform substitution on the type T with a given set of template
 /// arguments.
 ///
index 473dcb7d1fa693896e7e94eafffba62ebea0515e..dc5013c25d8e89cf3881c24be3c7cfffcbfd8478 100644 (file)
@@ -456,6 +456,10 @@ bool Sema::CheckParameterPacksForExpansion(SourceLocation EllipsisLoc,
   std::pair<IdentifierInfo *, SourceLocation> FirstPack;
   bool HaveFirstPack = false;
   
+  // FIXME: Variadic templates. Even if we don't expand, we'd still like to
+  // return the number of expansions back to the caller, perhaps as an 
+  // llvm::Optional, so that it can be embedded in the pack expansion. This
+  // is important for the multi-level substitution case.
   for (unsigned I = 0; I != NumUnexpanded; ++I) {
     // Compute the depth and index for this parameter pack.
     unsigned Depth;
index 43d89e2875ef5e7499bf4b7f3bb308ed80392b97..4127d5045ac822998f48db8f4b6257d151d6d4ea 100644 (file)
@@ -3926,6 +3926,13 @@ QualType TreeTransform<Derived>::TransformSubstTemplateTypeParmType(
   return TransformTypeSpecType(TLB, TL);
 }
 
+template<typename Derived>
+QualType TreeTransform<Derived>::TransformSubstTemplateTypeParmPackType(
+                                          TypeLocBuilder &TLB,
+                                          SubstTemplateTypeParmPackTypeLoc TL) {
+  return TransformTypeSpecType(TLB, TL);
+}
+
 template<typename Derived>
 QualType TreeTransform<Derived>::TransformTemplateSpecializationType(
                                                         TypeLocBuilder &TLB,
index 487e23f19c27571616b104294924ee690d887b2d..edb79ee808d9ed1ea3f3dcc43daf61f50f215423 100644 (file)
@@ -2997,6 +2997,15 @@ QualType ASTReader::ReadTypeRecord(unsigned Index) {
                                             Replacement);
   }
 
+  case TYPE_SUBST_TEMPLATE_TYPE_PARM_PACK: {
+    unsigned Idx = 0;
+    QualType Parm = GetType(Record[Idx++]);
+    TemplateArgument ArgPack = ReadTemplateArgument(*Loc.F, Record, Idx);
+    return Context->getSubstTemplateTypeParmPackType(
+                                               cast<TemplateTypeParmType>(Parm),
+                                                     ArgPack);
+  }
+
   case TYPE_INJECTED_CLASS_NAME: {
     CXXRecordDecl *D = cast<CXXRecordDecl>(GetDecl(Record[0]));
     QualType TST = GetType(Record[1]); // probably derivable
@@ -3233,6 +3242,10 @@ void TypeLocReader::VisitSubstTemplateTypeParmTypeLoc(
                                             SubstTemplateTypeParmTypeLoc TL) {
   TL.setNameLoc(ReadSourceLocation(Record, Idx));
 }
+void TypeLocReader::VisitSubstTemplateTypeParmPackTypeLoc(
+                                          SubstTemplateTypeParmPackTypeLoc TL) {
+  TL.setNameLoc(ReadSourceLocation(Record, Idx));
+}
 void TypeLocReader::VisitTemplateSpecializationTypeLoc(
                                            TemplateSpecializationTypeLoc TL) {
   TL.setTemplateNameLoc(ReadSourceLocation(Record, Idx));
index d3b103f56c1f704a51b5ede4a3b96cb88ba1d916..52cf3829dc9385b226c4d5b2efc8fc084f43fd77 100644 (file)
@@ -243,6 +243,14 @@ ASTTypeWriter::VisitSubstTemplateTypeParmType(
   Code = TYPE_SUBST_TEMPLATE_TYPE_PARM;
 }
 
+void
+ASTTypeWriter::VisitSubstTemplateTypeParmPackType(
+                                      const SubstTemplateTypeParmPackType *T) {
+  Writer.AddTypeRef(QualType(T->getReplacedParameter(), 0), Record);
+  Writer.AddTemplateArgument(T->getArgumentPack(), Record);
+  Code = TYPE_SUBST_TEMPLATE_TYPE_PARM_PACK;
+}
+
 void
 ASTTypeWriter::VisitTemplateSpecializationType(
                                        const TemplateSpecializationType *T) {
@@ -491,6 +499,10 @@ void TypeLocWriter::VisitSubstTemplateTypeParmTypeLoc(
                                             SubstTemplateTypeParmTypeLoc TL) {
   Writer.AddSourceLocation(TL.getNameLoc(), Record);
 }
+void TypeLocWriter::VisitSubstTemplateTypeParmPackTypeLoc(
+                                          SubstTemplateTypeParmPackTypeLoc TL) {
+  Writer.AddSourceLocation(TL.getNameLoc(), Record);
+}
 void TypeLocWriter::VisitTemplateSpecializationTypeLoc(
                                            TemplateSpecializationTypeLoc TL) {
   Writer.AddSourceLocation(TL.getTemplateNameLoc(), Record);
index 649c9b8ef267e959ab59ffbc845a1200c9a6ad3c..ff2134dc33c0e35f82ba2fea75853c4ada27690d 100644 (file)
@@ -11,3 +11,23 @@ struct X0 {
 void test_X0() {
   X0<int>().f<1, 2, 3, 4, 5>();
 }
+
+namespace PacksAtDifferentLevels {
+  template<typename...> struct tuple { };
+  template<typename T, typename U> struct pair { };
+
+  template<typename ...Types>
+  struct X {
+    template<typename> struct Inner;
+
+    template<typename ...YTypes>
+    struct Inner<tuple<pair<Types, YTypes>...> > {
+      static const unsigned zero = sizeof...(Types) - sizeof...(YTypes);
+    };
+  };
+
+  int check0[X<short, int, long>::Inner<tuple<pair<short, unsigned short>,
+                                             pair<int, unsigned int>,
+                                             pair<long, unsigned long>>
+                                       >::zero == 0? 1 : -1];
+}