forked from OSchip/llvm-project
				
			Support templateids in friend declarations. Fixes bug 4859.
llvm-svn: 81233
This commit is contained in:
		
							parent
							
								
									05b2f10e57
								
							
						
					
					
						commit
						d8fe9af3a2
					
				| 
						 | 
					@ -1591,21 +1591,34 @@ public:
 | 
				
			||||||
  ///
 | 
					  ///
 | 
				
			||||||
  /// \param Template  A template whose specialization results in a
 | 
					  /// \param Template  A template whose specialization results in a
 | 
				
			||||||
  /// type, e.g., a class template or template template parameter.
 | 
					  /// type, e.g., a class template or template template parameter.
 | 
				
			||||||
  /// 
 | 
					 | 
				
			||||||
  /// \param TagSpec The tag spec that was provided as part of the
 | 
					 | 
				
			||||||
  /// elaborated-type-specifier, or TST_unspecified if this was not
 | 
					 | 
				
			||||||
  /// an elaborated type.
 | 
					 | 
				
			||||||
  virtual TypeResult ActOnTemplateIdType(TemplateTy Template,
 | 
					  virtual TypeResult ActOnTemplateIdType(TemplateTy Template,
 | 
				
			||||||
                                         SourceLocation TemplateLoc,
 | 
					                                         SourceLocation TemplateLoc,
 | 
				
			||||||
                                         SourceLocation LAngleLoc,
 | 
					                                         SourceLocation LAngleLoc,
 | 
				
			||||||
                                         ASTTemplateArgsPtr TemplateArgs,
 | 
					                                         ASTTemplateArgsPtr TemplateArgs,
 | 
				
			||||||
                                         SourceLocation *TemplateArgLocs,
 | 
					                                         SourceLocation *TemplateArgLocs,
 | 
				
			||||||
                                         SourceLocation RAngleLoc,
 | 
					                                         SourceLocation RAngleLoc) {
 | 
				
			||||||
                        DeclSpec::TST TagSpec = DeclSpec::TST_unspecified,
 | 
					 | 
				
			||||||
                        SourceLocation TagLoc = SourceLocation()) {
 | 
					 | 
				
			||||||
    return TypeResult();
 | 
					    return TypeResult();
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /// \brief Note that a template ID was used with a tag.
 | 
				
			||||||
 | 
					  ///
 | 
				
			||||||
 | 
					  /// \param Type The result of ActOnTemplateIdType.
 | 
				
			||||||
 | 
					  ///
 | 
				
			||||||
 | 
					  /// \param TUK Either TUK_Reference or TUK_Friend.  Declarations and
 | 
				
			||||||
 | 
					  /// definitions are interpreted as explicit instantiations or
 | 
				
			||||||
 | 
					  /// specializations.
 | 
				
			||||||
 | 
					  /// 
 | 
				
			||||||
 | 
					  /// \param TagSpec The tag keyword that was provided as part of the
 | 
				
			||||||
 | 
					  /// elaborated-type-specifier;  either class, struct, union, or enum.
 | 
				
			||||||
 | 
					  ///
 | 
				
			||||||
 | 
					  /// \param TagLoc The location of the tag keyword.
 | 
				
			||||||
 | 
					  virtual TypeResult ActOnTagTemplateIdType(TypeResult Type,
 | 
				
			||||||
 | 
					                                            TagUseKind TUK,
 | 
				
			||||||
 | 
					                                            DeclSpec::TST TagSpec,
 | 
				
			||||||
 | 
					                                            SourceLocation TagLoc) {
 | 
				
			||||||
 | 
					    return TypeResult();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /// \brief Form a reference to a template-id (that will refer to a function)
 | 
					  /// \brief Form a reference to a template-id (that will refer to a function)
 | 
				
			||||||
  /// from a template and a list of template arguments.
 | 
					  /// from a template and a list of template arguments.
 | 
				
			||||||
  ///
 | 
					  ///
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -651,26 +651,27 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind,
 | 
				
			||||||
                                             TemplateId->RAngleLoc, 
 | 
					                                             TemplateId->RAngleLoc, 
 | 
				
			||||||
                                             Attr);
 | 
					                                             Attr);
 | 
				
			||||||
    } else if (TUK == Action::TUK_Reference || TUK == Action::TUK_Friend) {
 | 
					    } else if (TUK == Action::TUK_Reference || TUK == Action::TUK_Friend) {
 | 
				
			||||||
      Action::TypeResult TypeResult =
 | 
					      Action::TypeResult Type
 | 
				
			||||||
        Actions.ActOnTemplateIdType(TemplateTy::make(TemplateId->Template),
 | 
					        = Actions.ActOnTemplateIdType(TemplateTy::make(TemplateId->Template),
 | 
				
			||||||
                                    TemplateId->TemplateNameLoc,
 | 
					                                      TemplateId->TemplateNameLoc,
 | 
				
			||||||
                                    TemplateId->LAngleLoc,
 | 
					                                      TemplateId->LAngleLoc,
 | 
				
			||||||
                                    TemplateArgsPtr,
 | 
					                                      TemplateArgsPtr,
 | 
				
			||||||
                                    TemplateId->getTemplateArgLocations(),
 | 
					                                      TemplateId->getTemplateArgLocations(),
 | 
				
			||||||
                                    TemplateId->RAngleLoc,
 | 
					                                      TemplateId->RAngleLoc);
 | 
				
			||||||
                                    TagType, StartLoc);
 | 
					
 | 
				
			||||||
 | 
					      Type = Actions.ActOnTagTemplateIdType(Type, TUK, TagType, StartLoc);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      TemplateId->Destroy();
 | 
					      TemplateId->Destroy();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      if (TypeResult.isInvalid()) {
 | 
					      if (Type.isInvalid()) {
 | 
				
			||||||
        DS.SetTypeSpecError();
 | 
					        DS.SetTypeSpecError();
 | 
				
			||||||
        return;
 | 
					        return;
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
  
 | 
					
 | 
				
			||||||
      const char *PrevSpec = 0;
 | 
					      const char *PrevSpec = 0;
 | 
				
			||||||
      unsigned DiagID;
 | 
					      unsigned DiagID;
 | 
				
			||||||
      if (DS.SetTypeSpecType(DeclSpec::TST_typename, StartLoc, PrevSpec,
 | 
					      if (DS.SetTypeSpecType(DeclSpec::TST_typename, StartLoc, PrevSpec,
 | 
				
			||||||
                             DiagID, TypeResult.get()))
 | 
					                             DiagID, Type.get()))
 | 
				
			||||||
        Diag(StartLoc, DiagID) << PrevSpec;
 | 
					        Diag(StartLoc, DiagID) << PrevSpec;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      return;
 | 
					      return;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2401,10 +2401,13 @@ public:
 | 
				
			||||||
                      SourceLocation LAngleLoc,
 | 
					                      SourceLocation LAngleLoc,
 | 
				
			||||||
                      ASTTemplateArgsPtr TemplateArgs,
 | 
					                      ASTTemplateArgsPtr TemplateArgs,
 | 
				
			||||||
                      SourceLocation *TemplateArgLocs,
 | 
					                      SourceLocation *TemplateArgLocs,
 | 
				
			||||||
                      SourceLocation RAngleLoc,
 | 
					                      SourceLocation RAngleLoc);
 | 
				
			||||||
                      DeclSpec::TST TagSpec,
 | 
					
 | 
				
			||||||
                      SourceLocation TagLoc);
 | 
					  virtual TypeResult ActOnTagTemplateIdType(TypeResult Type,
 | 
				
			||||||
  
 | 
					                                            TagUseKind TUK,
 | 
				
			||||||
 | 
					                                            DeclSpec::TST TagSpec,
 | 
				
			||||||
 | 
					                                            SourceLocation TagLoc);
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
  OwningExprResult BuildTemplateIdExpr(TemplateName Template,
 | 
					  OwningExprResult BuildTemplateIdExpr(TemplateName Template,
 | 
				
			||||||
                                       SourceLocation TemplateNameLoc,
 | 
					                                       SourceLocation TemplateNameLoc,
 | 
				
			||||||
                                       SourceLocation LAngleLoc,
 | 
					                                       SourceLocation LAngleLoc,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3917,61 +3917,60 @@ Sema::DeclPtrTy Sema::ActOnFriendTypeDecl(Scope *S,
 | 
				
			||||||
  assert(DS.isFriendSpecified());
 | 
					  assert(DS.isFriendSpecified());
 | 
				
			||||||
  assert(DS.getStorageClassSpec() == DeclSpec::SCS_unspecified);
 | 
					  assert(DS.getStorageClassSpec() == DeclSpec::SCS_unspecified);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // Check to see if the decl spec was syntactically like "struct foo".
 | 
					  // Try to convert the decl specifier to a type.
 | 
				
			||||||
  RecordDecl *RD = NULL;
 | 
					  bool invalid = false;
 | 
				
			||||||
 | 
					  QualType T = ConvertDeclSpecToType(DS, Loc, invalid);
 | 
				
			||||||
  switch (DS.getTypeSpecType()) {
 | 
					  if (invalid) return DeclPtrTy();
 | 
				
			||||||
  case DeclSpec::TST_class:
 | 
					 | 
				
			||||||
  case DeclSpec::TST_struct:
 | 
					 | 
				
			||||||
  case DeclSpec::TST_union:
 | 
					 | 
				
			||||||
    RD = dyn_cast_or_null<CXXRecordDecl>((Decl*) DS.getTypeRep());
 | 
					 | 
				
			||||||
    if (!RD) return DeclPtrTy();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // The parser doesn't quite handle
 | 
					 | 
				
			||||||
    //   friend class A {}
 | 
					 | 
				
			||||||
    // as we'd like, because it might have been the (valid) prefix of
 | 
					 | 
				
			||||||
    //   friend class A {} foo();
 | 
					 | 
				
			||||||
    // So even in C++0x mode we don't want to 
 | 
					 | 
				
			||||||
    IsDefinition |= RD->isDefinition();
 | 
					 | 
				
			||||||
    break;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  default: break;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  FriendDecl::FriendUnion FU = RD;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // C++ [class.friend]p2:
 | 
					  // C++ [class.friend]p2:
 | 
				
			||||||
  //   An elaborated-type-specifier shall be used in a friend declaration
 | 
					  //   An elaborated-type-specifier shall be used in a friend declaration
 | 
				
			||||||
  //   for a class.*
 | 
					  //   for a class.*
 | 
				
			||||||
  //   * The class-key of the elaborated-type-specifier is required.
 | 
					  //   * The class-key of the elaborated-type-specifier is required.
 | 
				
			||||||
  // So if we didn't get a record decl above, we're invalid in C++98 mode.
 | 
					  // This is one of the rare places in Clang where it's legitimate to
 | 
				
			||||||
  if (!RD) {
 | 
					  // ask about the "spelling" of the type.
 | 
				
			||||||
    bool invalid = false;
 | 
					  if (!getLangOptions().CPlusPlus0x && !isa<ElaboratedType>(T)) {
 | 
				
			||||||
    QualType T = ConvertDeclSpecToType(DS, Loc, invalid);
 | 
					    // If we evaluated the type to a record type, suggest putting
 | 
				
			||||||
    if (invalid) return DeclPtrTy();
 | 
					    // a tag in front.
 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    if (const RecordType *RT = T->getAs<RecordType>()) {
 | 
					    if (const RecordType *RT = T->getAs<RecordType>()) {
 | 
				
			||||||
      FU = RD = cast<CXXRecordDecl>(RT->getDecl());
 | 
					      RecordDecl *RD = RT->getDecl();
 | 
				
			||||||
      
 | 
					
 | 
				
			||||||
      // Untagged typenames are invalid prior to C++0x, but we can
 | 
					      std::string InsertionText = std::string(" ") + RD->getKindName();
 | 
				
			||||||
      // suggest an easy fix which should work.
 | 
					
 | 
				
			||||||
      if (!getLangOptions().CPlusPlus0x) {
 | 
					      Diag(DS.getFriendSpecLoc(), diag::err_unelaborated_friend_type)
 | 
				
			||||||
        Diag(DS.getFriendSpecLoc(), diag::err_unelaborated_friend_type)
 | 
					        << (RD->isUnion())
 | 
				
			||||||
          << (RD->isUnion())
 | 
					        << CodeModificationHint::CreateInsertion(DS.getTypeSpecTypeLoc(),
 | 
				
			||||||
          << CodeModificationHint::CreateInsertion(DS.getTypeSpecTypeLoc(),
 | 
					                                                 InsertionText);
 | 
				
			||||||
                                        RD->isUnion() ? " union" : " class");
 | 
					 | 
				
			||||||
        return DeclPtrTy();
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    }else if (!getLangOptions().CPlusPlus0x) {
 | 
					 | 
				
			||||||
      Diag(DS.getFriendSpecLoc(), diag::err_unexpected_friend)
 | 
					 | 
				
			||||||
          << DS.getSourceRange();
 | 
					 | 
				
			||||||
      return DeclPtrTy();
 | 
					      return DeclPtrTy();
 | 
				
			||||||
    }else {
 | 
					    }else {
 | 
				
			||||||
      FU = T.getTypePtr();
 | 
					      Diag(DS.getFriendSpecLoc(), diag::err_unexpected_friend)
 | 
				
			||||||
 | 
					          << DS.getSourceRange();
 | 
				
			||||||
 | 
					      return DeclPtrTy();      
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  assert(FU && "should have a friend decl/type by here!");
 | 
					  FriendDecl::FriendUnion FU = T.getTypePtr();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // The parser doesn't quite handle
 | 
				
			||||||
 | 
					  //   friend class A { ... }
 | 
				
			||||||
 | 
					  // optimally, because it might have been the (valid) prefix of
 | 
				
			||||||
 | 
					  //   friend class A { ... } foo();
 | 
				
			||||||
 | 
					  // So in a very particular set of circumstances, we need to adjust
 | 
				
			||||||
 | 
					  // IsDefinition.
 | 
				
			||||||
 | 
					  //
 | 
				
			||||||
 | 
					  // Also, if we made a RecordDecl in ActOnTag, we want that to be the
 | 
				
			||||||
 | 
					  // object of our friend declaration.
 | 
				
			||||||
 | 
					  switch (DS.getTypeSpecType()) {
 | 
				
			||||||
 | 
					  default: break;
 | 
				
			||||||
 | 
					  case DeclSpec::TST_class:
 | 
				
			||||||
 | 
					  case DeclSpec::TST_struct:
 | 
				
			||||||
 | 
					  case DeclSpec::TST_union:
 | 
				
			||||||
 | 
					    CXXRecordDecl *RD = cast_or_null<CXXRecordDecl>((Decl*) DS.getTypeRep());
 | 
				
			||||||
 | 
					    if (RD) {
 | 
				
			||||||
 | 
					      IsDefinition |= RD->isDefinition();
 | 
				
			||||||
 | 
					      FU = RD;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    break;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // C++ [class.friend]p2: A class shall not be defined inside
 | 
					  // C++ [class.friend]p2: A class shall not be defined inside
 | 
				
			||||||
  //   a friend declaration.
 | 
					  //   a friend declaration.
 | 
				
			||||||
| 
						 | 
					@ -3986,11 +3985,10 @@ Sema::DeclPtrTy Sema::ActOnFriendTypeDecl(Scope *S,
 | 
				
			||||||
  // But that's a silly restriction which nobody implements for
 | 
					  // But that's a silly restriction which nobody implements for
 | 
				
			||||||
  // inner classes, and C++0x removes it anyway, so we only report
 | 
					  // inner classes, and C++0x removes it anyway, so we only report
 | 
				
			||||||
  // this (as a warning) if we're being pedantic.
 | 
					  // this (as a warning) if we're being pedantic.
 | 
				
			||||||
  if (!getLangOptions().CPlusPlus0x) {
 | 
					  if (!getLangOptions().CPlusPlus0x)
 | 
				
			||||||
    assert(RD && "must have a record decl in C++98 mode");
 | 
					    if (const RecordType *RT = T->getAs<RecordType>())
 | 
				
			||||||
    if (RD->getDeclContext() == CurContext)
 | 
					      if (RT->getDecl()->getDeclContext() == CurContext)
 | 
				
			||||||
      Diag(DS.getFriendSpecLoc(), diag::ext_friend_inner_class);
 | 
					        Diag(DS.getFriendSpecLoc(), diag::ext_friend_inner_class);
 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  FriendDecl *FD = FriendDecl::Create(Context, CurContext, Loc, FU,
 | 
					  FriendDecl *FD = FriendDecl::Create(Context, CurContext, Loc, FU,
 | 
				
			||||||
                                      DS.getFriendSpecLoc());
 | 
					                                      DS.getFriendSpecLoc());
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1121,9 +1121,7 @@ Sema::ActOnTemplateIdType(TemplateTy TemplateD, SourceLocation TemplateLoc,
 | 
				
			||||||
                          SourceLocation LAngleLoc, 
 | 
					                          SourceLocation LAngleLoc, 
 | 
				
			||||||
                          ASTTemplateArgsPtr TemplateArgsIn,
 | 
					                          ASTTemplateArgsPtr TemplateArgsIn,
 | 
				
			||||||
                          SourceLocation *TemplateArgLocs,
 | 
					                          SourceLocation *TemplateArgLocs,
 | 
				
			||||||
                          SourceLocation RAngleLoc,
 | 
					                          SourceLocation RAngleLoc) {
 | 
				
			||||||
                          DeclSpec::TST TagSpec,
 | 
					 | 
				
			||||||
                          SourceLocation TagLoc) {
 | 
					 | 
				
			||||||
  TemplateName Template = TemplateD.getAsVal<TemplateName>();
 | 
					  TemplateName Template = TemplateD.getAsVal<TemplateName>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // Translate the parser's template argument list in our AST format.
 | 
					  // Translate the parser's template argument list in our AST format.
 | 
				
			||||||
| 
						 | 
					@ -1139,26 +1137,38 @@ Sema::ActOnTemplateIdType(TemplateTy TemplateD, SourceLocation TemplateLoc,
 | 
				
			||||||
  if (Result.isNull())
 | 
					  if (Result.isNull())
 | 
				
			||||||
    return true;
 | 
					    return true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // If we were given a tag specifier, verify it.
 | 
					  return Result.getAsOpaquePtr();
 | 
				
			||||||
  if (TagSpec != DeclSpec::TST_unspecified) {
 | 
					}
 | 
				
			||||||
    TagDecl::TagKind TagKind = TagDecl::getTagKindForTypeSpec(TagSpec);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (const RecordType *T = Result->getAs<RecordType>()) {
 | 
					Sema::TypeResult Sema::ActOnTagTemplateIdType(TypeResult TypeResult,
 | 
				
			||||||
      RecordDecl *D = T->getDecl();
 | 
					                                              TagUseKind TUK,
 | 
				
			||||||
 | 
					                                              DeclSpec::TST TagSpec,
 | 
				
			||||||
 | 
					                                              SourceLocation TagLoc) {
 | 
				
			||||||
 | 
					  if (TypeResult.isInvalid())
 | 
				
			||||||
 | 
					    return Sema::TypeResult();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      IdentifierInfo *Id = D->getIdentifier();
 | 
					  QualType Type = QualType::getFromOpaquePtr(TypeResult.get());
 | 
				
			||||||
      assert(Id && "templated class must have an identifier");
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
      if (!isAcceptableTagRedeclaration(D, TagKind, TagLoc, *Id)) {
 | 
					  // Verify the tag specifier.
 | 
				
			||||||
        Diag(TagLoc, diag::err_use_with_wrong_tag)
 | 
					  TagDecl::TagKind TagKind = TagDecl::getTagKindForTypeSpec(TagSpec);
 | 
				
			||||||
          << Id
 | 
					  
 | 
				
			||||||
          << CodeModificationHint::CreateReplacement(SourceRange(TagLoc),
 | 
					  if (const RecordType *RT = Type->getAs<RecordType>()) {
 | 
				
			||||||
                                                     D->getKindName());
 | 
					    RecordDecl *D = RT->getDecl();
 | 
				
			||||||
      }
 | 
					
 | 
				
			||||||
 | 
					    IdentifierInfo *Id = D->getIdentifier();
 | 
				
			||||||
 | 
					    assert(Id && "templated class must have an identifier");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (!isAcceptableTagRedeclaration(D, TagKind, TagLoc, *Id)) {
 | 
				
			||||||
 | 
					      Diag(TagLoc, diag::err_use_with_wrong_tag)
 | 
				
			||||||
 | 
					        << Id
 | 
				
			||||||
 | 
					        << CodeModificationHint::CreateReplacement(SourceRange(TagLoc),
 | 
				
			||||||
 | 
					                                                   D->getKindName());
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return Result.getAsOpaquePtr();
 | 
					  QualType ElabType = Context.getElaboratedType(Type, TagKind);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return ElabType.getAsOpaquePtr();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Sema::OwningExprResult Sema::BuildTemplateIdExpr(TemplateName Template,
 | 
					Sema::OwningExprResult Sema::BuildTemplateIdExpr(TemplateName Template,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -36,6 +36,11 @@ class A {
 | 
				
			||||||
  template <typename T> friend bool iszero(const A &a) throw();
 | 
					  template <typename T> friend bool iszero(const A &a) throw();
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					template <class T> class B_iterator;
 | 
				
			||||||
 | 
					template <class T> class B {
 | 
				
			||||||
 | 
					  friend class B_iterator<T>;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int calc1() {
 | 
					int calc1() {
 | 
				
			||||||
  Num<int> left = -1;
 | 
					  Num<int> left = -1;
 | 
				
			||||||
  Num<int> right = 1;
 | 
					  Num<int> right = 1;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue