Sema.cpp revision 8c465e6494d16a19127873dc9bdc55177ac6b6fd
1//===--- Sema.cpp - AST Builder and Semantic Analysis Implementation ------===//
2//
3//                     The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9//
10// This file implements the actions class which performs semantic analysis and
11// builds an AST out of a parse stream.
12//
13//===----------------------------------------------------------------------===//
14
15#include "clang/Sema/SemaInternal.h"
16#include "clang/Sema/DelayedDiagnostic.h"
17#include "TargetAttributesSema.h"
18#include "llvm/ADT/DenseMap.h"
19#include "llvm/ADT/SmallSet.h"
20#include "llvm/ADT/APFloat.h"
21#include "clang/Sema/CXXFieldCollector.h"
22#include "clang/Sema/ExternalSemaSource.h"
23#include "clang/Sema/PrettyDeclStackTrace.h"
24#include "clang/Sema/Scope.h"
25#include "clang/Sema/ScopeInfo.h"
26#include "clang/Sema/SemaConsumer.h"
27#include "clang/AST/ASTContext.h"
28#include "clang/AST/ASTDiagnostic.h"
29#include "clang/AST/DeclCXX.h"
30#include "clang/AST/DeclObjC.h"
31#include "clang/AST/Expr.h"
32#include "clang/Lex/Preprocessor.h"
33#include "clang/Basic/PartialDiagnostic.h"
34#include "clang/Basic/TargetInfo.h"
35using namespace clang;
36using namespace sema;
37
38FunctionScopeInfo::~FunctionScopeInfo() { }
39
40void FunctionScopeInfo::Clear(unsigned NumErrors) {
41  HasBranchProtectedScope = false;
42  HasBranchIntoScope = false;
43  HasIndirectGoto = false;
44
45  LabelMap.clear();
46  SwitchStack.clear();
47  Returns.clear();
48  NumErrorsAtStartOfFunction = NumErrors;
49}
50
51BlockScopeInfo::~BlockScopeInfo() { }
52
53void Sema::ActOnTranslationUnitScope(Scope *S) {
54  TUScope = S;
55  PushDeclContext(S, Context.getTranslationUnitDecl());
56
57  VAListTagName = PP.getIdentifierInfo("__va_list_tag");
58
59  if (!Context.isInt128Installed() && // May be set by ASTReader.
60      PP.getTargetInfo().getPointerWidth(0) >= 64) {
61    TypeSourceInfo *TInfo;
62
63    // Install [u]int128_t for 64-bit targets.
64    TInfo = Context.getTrivialTypeSourceInfo(Context.Int128Ty);
65    PushOnScopeChains(TypedefDecl::Create(Context, CurContext,
66                                          SourceLocation(),
67                                          &Context.Idents.get("__int128_t"),
68                                          TInfo), TUScope);
69
70    TInfo = Context.getTrivialTypeSourceInfo(Context.UnsignedInt128Ty);
71    PushOnScopeChains(TypedefDecl::Create(Context, CurContext,
72                                          SourceLocation(),
73                                          &Context.Idents.get("__uint128_t"),
74                                          TInfo), TUScope);
75    Context.setInt128Installed();
76  }
77
78
79  if (!PP.getLangOptions().ObjC1) return;
80
81  // Built-in ObjC types may already be set by ASTReader (hence isNull checks).
82  if (Context.getObjCSelType().isNull()) {
83    // Create the built-in typedef for 'SEL'.
84    QualType SelT = Context.getPointerType(Context.ObjCBuiltinSelTy);
85    TypeSourceInfo *SelInfo = Context.getTrivialTypeSourceInfo(SelT);
86    TypedefDecl *SelTypedef
87      = TypedefDecl::Create(Context, CurContext, SourceLocation(),
88                            &Context.Idents.get("SEL"), SelInfo);
89    PushOnScopeChains(SelTypedef, TUScope);
90    Context.setObjCSelType(Context.getTypeDeclType(SelTypedef));
91    Context.ObjCSelRedefinitionType = Context.getObjCSelType();
92  }
93
94  // Synthesize "@class Protocol;
95  if (Context.getObjCProtoType().isNull()) {
96    ObjCInterfaceDecl *ProtocolDecl =
97      ObjCInterfaceDecl::Create(Context, CurContext, SourceLocation(),
98                                &Context.Idents.get("Protocol"),
99                                SourceLocation(), true);
100    Context.setObjCProtoType(Context.getObjCInterfaceType(ProtocolDecl));
101    PushOnScopeChains(ProtocolDecl, TUScope, false);
102  }
103  // Create the built-in typedef for 'id'.
104  if (Context.getObjCIdType().isNull()) {
105    QualType T = Context.getObjCObjectType(Context.ObjCBuiltinIdTy, 0, 0);
106    T = Context.getObjCObjectPointerType(T);
107    TypeSourceInfo *IdInfo = Context.getTrivialTypeSourceInfo(T);
108    TypedefDecl *IdTypedef
109      = TypedefDecl::Create(Context, CurContext, SourceLocation(),
110                            &Context.Idents.get("id"), IdInfo);
111    PushOnScopeChains(IdTypedef, TUScope);
112    Context.setObjCIdType(Context.getTypeDeclType(IdTypedef));
113    Context.ObjCIdRedefinitionType = Context.getObjCIdType();
114  }
115  // Create the built-in typedef for 'Class'.
116  if (Context.getObjCClassType().isNull()) {
117    QualType T = Context.getObjCObjectType(Context.ObjCBuiltinClassTy, 0, 0);
118    T = Context.getObjCObjectPointerType(T);
119    TypeSourceInfo *ClassInfo = Context.getTrivialTypeSourceInfo(T);
120    TypedefDecl *ClassTypedef
121      = TypedefDecl::Create(Context, CurContext, SourceLocation(),
122                            &Context.Idents.get("Class"), ClassInfo);
123    PushOnScopeChains(ClassTypedef, TUScope);
124    Context.setObjCClassType(Context.getTypeDeclType(ClassTypedef));
125    Context.ObjCClassRedefinitionType = Context.getObjCClassType();
126  }
127}
128
129Sema::Sema(Preprocessor &pp, ASTContext &ctxt, ASTConsumer &consumer,
130           bool CompleteTranslationUnit,
131           CodeCompleteConsumer *CodeCompleter)
132  : TheTargetAttributesSema(0),
133    LangOpts(pp.getLangOptions()), PP(pp), Context(ctxt), Consumer(consumer),
134    Diags(PP.getDiagnostics()), SourceMgr(PP.getSourceManager()),
135    ExternalSource(0), CodeCompleter(CodeCompleter), CurContext(0),
136    PackContext(0), VisContext(0), ParsingDeclDepth(0),
137    IdResolver(pp.getLangOptions()), GlobalNewDeleteDeclared(false),
138    CompleteTranslationUnit(CompleteTranslationUnit),
139    NumSFINAEErrors(0), SuppressAccessChecking(false),
140    NonInstantiationEntries(0), CurrentInstantiationScope(0), TyposCorrected(0),
141    AnalysisWarnings(*this)
142{
143  TUScope = 0;
144  if (getLangOptions().CPlusPlus)
145    FieldCollector.reset(new CXXFieldCollector());
146
147  // Tell diagnostics how to render things from the AST library.
148  PP.getDiagnostics().SetArgToStringFn(&FormatASTNodeDiagnosticArgument,
149                                       &Context);
150
151  ExprEvalContexts.push_back(
152                  ExpressionEvaluationContextRecord(PotentiallyEvaluated, 0));
153
154  FunctionScopes.push_back(new FunctionScopeInfo(Diags.getNumErrors()));
155}
156
157void Sema::Initialize() {
158  // Tell the AST consumer about this Sema object.
159  Consumer.Initialize(Context);
160
161  // FIXME: Isn't this redundant with the initialization above?
162  if (SemaConsumer *SC = dyn_cast<SemaConsumer>(&Consumer))
163    SC->InitializeSema(*this);
164
165  // Tell the external Sema source about this Sema object.
166  if (ExternalSemaSource *ExternalSema
167      = dyn_cast_or_null<ExternalSemaSource>(Context.getExternalSource()))
168    ExternalSema->InitializeSema(*this);
169}
170
171Sema::~Sema() {
172  if (PackContext) FreePackedContext();
173  if (VisContext) FreeVisContext();
174  delete TheTargetAttributesSema;
175
176  // Kill all the active scopes.
177  for (unsigned I = 1, E = FunctionScopes.size(); I != E; ++I)
178    delete FunctionScopes[I];
179  if (FunctionScopes.size() == 1)
180    delete FunctionScopes[0];
181
182  // Tell the SemaConsumer to forget about us; we're going out of scope.
183  if (SemaConsumer *SC = dyn_cast<SemaConsumer>(&Consumer))
184    SC->ForgetSema();
185
186  // Detach from the external Sema source.
187  if (ExternalSemaSource *ExternalSema
188        = dyn_cast_or_null<ExternalSemaSource>(Context.getExternalSource()))
189    ExternalSema->ForgetSema();
190}
191
192/// ImpCastExprToType - If Expr is not of type 'Type', insert an implicit cast.
193/// If there is already an implicit cast, merge into the existing one.
194/// The result is of the given category.
195void Sema::ImpCastExprToType(Expr *&Expr, QualType Ty,
196                             CastKind Kind, ExprValueKind VK,
197                             const CXXCastPath *BasePath) {
198  QualType ExprTy = Context.getCanonicalType(Expr->getType());
199  QualType TypeTy = Context.getCanonicalType(Ty);
200
201  if (ExprTy == TypeTy)
202    return;
203
204  if (Expr->getType()->isPointerType() && Ty->isPointerType()) {
205    QualType ExprBaseType = cast<PointerType>(ExprTy)->getPointeeType();
206    QualType BaseType = cast<PointerType>(TypeTy)->getPointeeType();
207    if (ExprBaseType.getAddressSpace() != BaseType.getAddressSpace()) {
208      Diag(Expr->getExprLoc(), diag::err_implicit_pointer_address_space_cast)
209        << Expr->getSourceRange();
210    }
211  }
212
213  // If this is a derived-to-base cast to a through a virtual base, we
214  // need a vtable.
215  if (Kind == CK_DerivedToBase &&
216      BasePathInvolvesVirtualBase(*BasePath)) {
217    QualType T = Expr->getType();
218    if (const PointerType *Pointer = T->getAs<PointerType>())
219      T = Pointer->getPointeeType();
220    if (const RecordType *RecordTy = T->getAs<RecordType>())
221      MarkVTableUsed(Expr->getLocStart(),
222                     cast<CXXRecordDecl>(RecordTy->getDecl()));
223  }
224
225  if (ImplicitCastExpr *ImpCast = dyn_cast<ImplicitCastExpr>(Expr)) {
226    if (ImpCast->getCastKind() == Kind && (!BasePath || BasePath->empty())) {
227      ImpCast->setType(Ty);
228      ImpCast->setValueKind(VK);
229      return;
230    }
231  }
232
233  Expr = ImplicitCastExpr::Create(Context, Ty, Kind, Expr, BasePath, VK);
234}
235
236ExprValueKind Sema::CastCategory(Expr *E) {
237  Expr::Classification Classification = E->Classify(Context);
238  return Classification.isRValue() ? VK_RValue :
239      (Classification.isLValue() ? VK_LValue : VK_XValue);
240}
241
242/// \brief Used to prune the decls of Sema's UnusedFileScopedDecls vector.
243static bool ShouldRemoveFromUnused(Sema *SemaRef, const DeclaratorDecl *D) {
244  if (D->isUsed())
245    return true;
246
247  if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) {
248    // UnusedFileScopedDecls stores the first declaration.
249    // The declaration may have become definition so check again.
250    const FunctionDecl *DeclToCheck;
251    if (FD->hasBody(DeclToCheck))
252      return !SemaRef->ShouldWarnIfUnusedFileScopedDecl(DeclToCheck);
253
254    // Later redecls may add new information resulting in not having to warn,
255    // so check again.
256    DeclToCheck = FD->getMostRecentDeclaration();
257    if (DeclToCheck != FD)
258      return !SemaRef->ShouldWarnIfUnusedFileScopedDecl(DeclToCheck);
259  }
260
261  if (const VarDecl *VD = dyn_cast<VarDecl>(D)) {
262    // UnusedFileScopedDecls stores the first declaration.
263    // The declaration may have become definition so check again.
264    const VarDecl *DeclToCheck = VD->getDefinition();
265    if (DeclToCheck)
266      return !SemaRef->ShouldWarnIfUnusedFileScopedDecl(DeclToCheck);
267
268    // Later redecls may add new information resulting in not having to warn,
269    // so check again.
270    DeclToCheck = VD->getMostRecentDeclaration();
271    if (DeclToCheck != VD)
272      return !SemaRef->ShouldWarnIfUnusedFileScopedDecl(DeclToCheck);
273  }
274
275  return false;
276}
277
278/// ActOnEndOfTranslationUnit - This is called at the very end of the
279/// translation unit when EOF is reached and all but the top-level scope is
280/// popped.
281void Sema::ActOnEndOfTranslationUnit() {
282  // At PCH writing, implicit instantiations and VTable handling info are
283  // stored and performed when the PCH is included.
284  if (CompleteTranslationUnit)
285    while (1) {
286      // C++: Perform implicit template instantiations.
287      //
288      // FIXME: When we perform these implicit instantiations, we do not
289      // carefully keep track of the point of instantiation (C++ [temp.point]).
290      // This means that name lookup that occurs within the template
291      // instantiation will always happen at the end of the translation unit,
292      // so it will find some names that should not be found. Although this is
293      // common behavior for C++ compilers, it is technically wrong. In the
294      // future, we either need to be able to filter the results of name lookup
295      // or we need to perform template instantiations earlier.
296      PerformPendingInstantiations();
297
298      /// If DefinedUsedVTables ends up marking any virtual member
299      /// functions it might lead to more pending template
300      /// instantiations, which is why we need to loop here.
301      if (!DefineUsedVTables())
302        break;
303    }
304
305  // Remove file scoped decls that turned out to be used.
306  UnusedFileScopedDecls.erase(std::remove_if(UnusedFileScopedDecls.begin(),
307                                             UnusedFileScopedDecls.end(),
308                              std::bind1st(std::ptr_fun(ShouldRemoveFromUnused),
309                                           this)),
310                              UnusedFileScopedDecls.end());
311
312  if (!CompleteTranslationUnit) {
313    TUScope = 0;
314    return;
315  }
316
317  // Check for #pragma weak identifiers that were never declared
318  // FIXME: This will cause diagnostics to be emitted in a non-determinstic
319  // order!  Iterating over a densemap like this is bad.
320  for (llvm::DenseMap<IdentifierInfo*,WeakInfo>::iterator
321       I = WeakUndeclaredIdentifiers.begin(),
322       E = WeakUndeclaredIdentifiers.end(); I != E; ++I) {
323    if (I->second.getUsed()) continue;
324
325    Diag(I->second.getLocation(), diag::warn_weak_identifier_undeclared)
326      << I->first;
327  }
328
329  // C99 6.9.2p2:
330  //   A declaration of an identifier for an object that has file
331  //   scope without an initializer, and without a storage-class
332  //   specifier or with the storage-class specifier static,
333  //   constitutes a tentative definition. If a translation unit
334  //   contains one or more tentative definitions for an identifier,
335  //   and the translation unit contains no external definition for
336  //   that identifier, then the behavior is exactly as if the
337  //   translation unit contains a file scope declaration of that
338  //   identifier, with the composite type as of the end of the
339  //   translation unit, with an initializer equal to 0.
340  llvm::SmallSet<VarDecl *, 32> Seen;
341  for (unsigned i = 0, e = TentativeDefinitions.size(); i != e; ++i) {
342    VarDecl *VD = TentativeDefinitions[i]->getActingDefinition();
343
344    // If the tentative definition was completed, getActingDefinition() returns
345    // null. If we've already seen this variable before, insert()'s second
346    // return value is false.
347    if (VD == 0 || VD->isInvalidDecl() || !Seen.insert(VD))
348      continue;
349
350    if (const IncompleteArrayType *ArrayT
351        = Context.getAsIncompleteArrayType(VD->getType())) {
352      if (RequireCompleteType(VD->getLocation(),
353                              ArrayT->getElementType(),
354                              diag::err_tentative_def_incomplete_type_arr)) {
355        VD->setInvalidDecl();
356        continue;
357      }
358
359      // Set the length of the array to 1 (C99 6.9.2p5).
360      Diag(VD->getLocation(), diag::warn_tentative_incomplete_array);
361      llvm::APInt One(Context.getTypeSize(Context.getSizeType()), true);
362      QualType T = Context.getConstantArrayType(ArrayT->getElementType(),
363                                                One, ArrayType::Normal, 0);
364      VD->setType(T);
365    } else if (RequireCompleteType(VD->getLocation(), VD->getType(),
366                                   diag::err_tentative_def_incomplete_type))
367      VD->setInvalidDecl();
368
369    // Notify the consumer that we've completed a tentative definition.
370    if (!VD->isInvalidDecl())
371      Consumer.CompleteTentativeDefinition(VD);
372
373  }
374
375  // Output warning for unused file scoped decls.
376  for (llvm::SmallVectorImpl<const DeclaratorDecl*>::iterator
377         I = UnusedFileScopedDecls.begin(),
378         E = UnusedFileScopedDecls.end(); I != E; ++I) {
379    if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(*I)) {
380      const FunctionDecl *DiagD;
381      if (!FD->hasBody(DiagD))
382        DiagD = FD;
383      Diag(DiagD->getLocation(),
384           isa<CXXMethodDecl>(DiagD) ? diag::warn_unused_member_function
385                                     : diag::warn_unused_function)
386            << DiagD->getDeclName();
387    } else {
388      const VarDecl *DiagD = cast<VarDecl>(*I)->getDefinition();
389      if (!DiagD)
390        DiagD = cast<VarDecl>(*I);
391      Diag(DiagD->getLocation(), diag::warn_unused_variable)
392            << DiagD->getDeclName();
393    }
394  }
395
396  TUScope = 0;
397}
398
399
400//===----------------------------------------------------------------------===//
401// Helper functions.
402//===----------------------------------------------------------------------===//
403
404DeclContext *Sema::getFunctionLevelDeclContext() {
405  DeclContext *DC = CurContext;
406
407  while (isa<BlockDecl>(DC) || isa<EnumDecl>(DC))
408    DC = DC->getParent();
409
410  return DC;
411}
412
413/// getCurFunctionDecl - If inside of a function body, this returns a pointer
414/// to the function decl for the function being parsed.  If we're currently
415/// in a 'block', this returns the containing context.
416FunctionDecl *Sema::getCurFunctionDecl() {
417  DeclContext *DC = getFunctionLevelDeclContext();
418  return dyn_cast<FunctionDecl>(DC);
419}
420
421ObjCMethodDecl *Sema::getCurMethodDecl() {
422  DeclContext *DC = getFunctionLevelDeclContext();
423  return dyn_cast<ObjCMethodDecl>(DC);
424}
425
426NamedDecl *Sema::getCurFunctionOrMethodDecl() {
427  DeclContext *DC = getFunctionLevelDeclContext();
428  if (isa<ObjCMethodDecl>(DC) || isa<FunctionDecl>(DC))
429    return cast<NamedDecl>(DC);
430  return 0;
431}
432
433Sema::SemaDiagnosticBuilder::~SemaDiagnosticBuilder() {
434  if (!this->Emit())
435    return;
436
437  // If this is not a note, and we're in a template instantiation
438  // that is different from the last template instantiation where
439  // we emitted an error, print a template instantiation
440  // backtrace.
441  if (!SemaRef.Diags.isBuiltinNote(DiagID) &&
442      !SemaRef.ActiveTemplateInstantiations.empty() &&
443      SemaRef.ActiveTemplateInstantiations.back()
444        != SemaRef.LastTemplateInstantiationErrorContext) {
445    SemaRef.PrintInstantiationStack();
446    SemaRef.LastTemplateInstantiationErrorContext
447      = SemaRef.ActiveTemplateInstantiations.back();
448  }
449}
450
451Sema::SemaDiagnosticBuilder Sema::Diag(SourceLocation Loc, unsigned DiagID) {
452  if (isSFINAEContext()) {
453    switch (Diagnostic::getDiagnosticSFINAEResponse(DiagID)) {
454    case Diagnostic::SFINAE_Report:
455      // Fall through; we'll report the diagnostic below.
456      break;
457
458    case Diagnostic::SFINAE_SubstitutionFailure:
459      // Count this failure so that we know that template argument deduction
460      // has failed.
461      ++NumSFINAEErrors;
462      // Fall through
463
464    case Diagnostic::SFINAE_Suppress:
465      // Suppress this diagnostic.
466      Diags.setLastDiagnosticIgnored();
467      return SemaDiagnosticBuilder(*this);
468    }
469  }
470
471  DiagnosticBuilder DB = Diags.Report(FullSourceLoc(Loc, SourceMgr), DiagID);
472  return SemaDiagnosticBuilder(DB, *this, DiagID);
473}
474
475Sema::SemaDiagnosticBuilder
476Sema::Diag(SourceLocation Loc, const PartialDiagnostic& PD) {
477  SemaDiagnosticBuilder Builder(Diag(Loc, PD.getDiagID()));
478  PD.Emit(Builder);
479
480  return Builder;
481}
482
483/// \brief Determines the active Scope associated with the given declaration
484/// context.
485///
486/// This routine maps a declaration context to the active Scope object that
487/// represents that declaration context in the parser. It is typically used
488/// from "scope-less" code (e.g., template instantiation, lazy creation of
489/// declarations) that injects a name for name-lookup purposes and, therefore,
490/// must update the Scope.
491///
492/// \returns The scope corresponding to the given declaraion context, or NULL
493/// if no such scope is open.
494Scope *Sema::getScopeForContext(DeclContext *Ctx) {
495
496  if (!Ctx)
497    return 0;
498
499  Ctx = Ctx->getPrimaryContext();
500  for (Scope *S = getCurScope(); S; S = S->getParent()) {
501    // Ignore scopes that cannot have declarations. This is important for
502    // out-of-line definitions of static class members.
503    if (S->getFlags() & (Scope::DeclScope | Scope::TemplateParamScope))
504      if (DeclContext *Entity = static_cast<DeclContext *> (S->getEntity()))
505        if (Ctx == Entity->getPrimaryContext())
506          return S;
507  }
508
509  return 0;
510}
511
512/// \brief Enter a new function scope
513void Sema::PushFunctionScope() {
514  if (FunctionScopes.size() == 1) {
515    // Use the "top" function scope rather than having to allocate
516    // memory for a new scope.
517    FunctionScopes.back()->Clear(getDiagnostics().getNumErrors());
518    FunctionScopes.push_back(FunctionScopes.back());
519    return;
520  }
521
522  FunctionScopes.push_back(
523                      new FunctionScopeInfo(getDiagnostics().getNumErrors()));
524}
525
526void Sema::PushBlockScope(Scope *BlockScope, BlockDecl *Block) {
527  FunctionScopes.push_back(new BlockScopeInfo(getDiagnostics().getNumErrors(),
528                                              BlockScope, Block));
529}
530
531void Sema::PopFunctionOrBlockScope() {
532  FunctionScopeInfo *Scope = FunctionScopes.pop_back_val();
533  assert(!FunctionScopes.empty() && "mismatched push/pop!");
534  if (FunctionScopes.back() != Scope)
535    delete Scope;
536}
537
538/// \brief Determine whether any errors occurred within this function/method/
539/// block.
540bool Sema::hasAnyErrorsInThisFunction() const {
541  return getCurFunction()->NumErrorsAtStartOfFunction
542             != getDiagnostics().getNumErrors();
543}
544
545BlockScopeInfo *Sema::getCurBlock() {
546  if (FunctionScopes.empty())
547    return 0;
548
549  return dyn_cast<BlockScopeInfo>(FunctionScopes.back());
550}
551
552// Pin this vtable to this file.
553ExternalSemaSource::~ExternalSemaSource() {}
554
555void PrettyDeclStackTraceEntry::print(llvm::raw_ostream &OS) const {
556  SourceLocation Loc = this->Loc;
557  if (!Loc.isValid() && TheDecl) Loc = TheDecl->getLocation();
558  if (Loc.isValid()) {
559    Loc.print(OS, S.getSourceManager());
560    OS << ": ";
561  }
562  OS << Message;
563
564  if (TheDecl && isa<NamedDecl>(TheDecl)) {
565    std::string Name = cast<NamedDecl>(TheDecl)->getNameAsString();
566    if (!Name.empty())
567      OS << " '" << Name << '\'';
568  }
569
570  OS << '\n';
571}
572