Commit 5d78b78c authored by Guillot Tony's avatar Guillot Tony Committed by Aaron Ballman
Browse files

[C2X] N3007 Type inference for object definitions

This patches implements the auto keyword from the N3007 standard
specification.
This allows deducing the type of the variable like in C++:
```
auto nb = 1;
auto chr = 'A';
auto str = "String";
```
The list of statements which allows the usage of auto:

    * Basic variables declarations (int, float, double, char, char*...)
    * Macros declaring a variable with the auto type

The list of statements which will not work with the auto keyword:

    * auto arrays
    * sizeof(), alignas()
    * auto parameters, auto return type
    * auto as a struct/typedef member
    * uninitialized auto variables
    * auto in an union
    * auto as a enum type specifier
    * auto casts
    * auto in an compound literals

Differential Revision: https://reviews.llvm.org/D133289
parent 58678d3b
Loading
Loading
Loading
Loading
+10 −4
Original line number Diff line number Diff line
@@ -240,6 +240,10 @@ def ext_imaginary_constant : Extension<
  "imaginary constants are a GNU extension">, InGroup<GNUImaginaryConstant>;
def ext_integer_complex : Extension<
  "complex integer types are a GNU extension">, InGroup<GNUComplexInteger>;
def ext_c23_auto_non_plain_identifier : Extension<
  "type inference of a declaration other than a plain identifier with optional "
  "trailing attributes is a Clang extension">,
  InGroup<DiagGroup<"auto-decl-extensions">>;
def err_invalid_saturation_spec : Error<"'_Sat' specifier is only valid on "
  "'_Fract' or '_Accum', not '%0'">;
@@ -2388,7 +2392,8 @@ def err_auto_not_allowed : Error<
  "|in conversion function type|here|in lambda parameter"
  "|in type allocated by 'new'|in K&R-style function parameter"
  "|in template parameter|in friend declaration|in function prototype that is "
  "not a function declaration|in requires expression parameter}1">;
  "not a function declaration|in requires expression parameter"
  "|in array declaration}1">;
def err_dependent_deduced_tst : Error<
  "typename specifier refers to "
  "%select{class template|function template|variable template|alias template|"
@@ -2461,7 +2466,8 @@ def err_implied_std_initializer_list_not_found : Error<
def err_malformed_std_initializer_list : Error<
  "std::initializer_list must be a class template with a single type parameter">;
def err_auto_init_list_from_c : Error<
  "cannot use __auto_type with initializer list in C">;
  "cannot use %select{'auto'|<ERROR>|'__auto_type'}0 with "
  "%select{initializer list|array}1 in C">;
def err_auto_bitfield : Error<
  "cannot pass bit-field as __auto_type initializer in C">;
@@ -6664,8 +6670,8 @@ def err_func_def_incomplete_result : Error<
def err_atomic_specifier_bad_type
    : Error<"_Atomic cannot be applied to "
            "%select{incomplete |array |function |reference |atomic |qualified "
            "|sizeless ||integer }0type "
            "%1 %select{|||||||which is not trivially copyable|}0">;
            "|sizeless ||integer |}0type "
            "%1 %select{|||||||which is not trivially copyable||in C23}0">;
def warn_atomic_member_access : Warning<
  "accessing a member of an atomic structure or union is undefined behavior">,
  InGroup<DiagGroup<"atomic-access">>, DefaultError;
+1 −1
Original line number Diff line number Diff line
@@ -4038,7 +4038,7 @@ void Parser::ParseDeclarationSpecifiers(
      isStorageClass = true;
      break;
    case tok::kw_auto:
      if (getLangOpts().CPlusPlus11) {
      if (getLangOpts().CPlusPlus11 || getLangOpts().C23) {
        if (isKnownToBeTypeSpecifier(GetLookAheadToken(1))) {
          isInvalid = DS.SetStorageClassSpec(Actions, DeclSpec::SCS_auto, Loc,
                                             PrevSpec, DiagID, Policy);
+3 −2
Original line number Diff line number Diff line
@@ -1375,8 +1375,9 @@ void DeclSpec::Finish(Sema &S, const PrintingPolicy &Policy) {
    StorageClassSpecLoc = SourceLocation();
  }
  // Diagnose if we've recovered from an ill-formed 'auto' storage class
  // specifier in a pre-C++11 dialect of C++.
  if (!S.getLangOpts().CPlusPlus11 && TypeSpecType == TST_auto)
  // specifier in a pre-C++11 dialect of C++ or in a pre-C23 dialect of C.
  if (!S.getLangOpts().CPlusPlus11 && !S.getLangOpts().C23 &&
      TypeSpecType == TST_auto)
    S.Diag(TSTLoc, diag::ext_auto_type_specifier);
  if (S.getLangOpts().CPlusPlus && !S.getLangOpts().CPlusPlus11 &&
      StorageClassSpec == SCS_auto)
+9 −0
Original line number Diff line number Diff line
@@ -12863,6 +12863,15 @@ QualType Sema::deduceVarTypeFromInitializer(VarDecl *VDecl,
  DeducedType *Deduced = Type->getContainedDeducedType();
  assert(Deduced && "deduceVarTypeFromInitializer for non-deduced type");
  // Diagnose auto array declarations in C23, unless it's a supported extension.
  if (getLangOpts().C23 && Type->isArrayType() &&
      !isa_and_present<StringLiteral, InitListExpr>(Init)) {
      Diag(Range.getBegin(), diag::err_auto_not_allowed)
          << (int)Deduced->getContainedAutoType()->getKeyword()
          << /*in array decl*/ 23 << Range;
    return QualType();
  }
  // C++11 [dcl.spec.auto]p3
  if (!Init) {
    assert(VDecl && "no init for init capture deduction?");
+17 −1
Original line number Diff line number Diff line
@@ -4842,9 +4842,25 @@ Sema::DeduceAutoType(TypeLoc Type, Expr *Init, QualType &Result,
    return TDK_Success;
  }

  // Make sure that we treat 'char[]' equaly as 'char*' in C23 mode.
  auto *String = dyn_cast<StringLiteral>(Init);
  if (getLangOpts().C23 && String && Type.getType()->isArrayType()) {
    Diag(Type.getBeginLoc(), diag::ext_c23_auto_non_plain_identifier);
    TypeLoc TL = TypeLoc(Init->getType(), Type.getOpaqueData());
    Result = SubstituteDeducedTypeTransform(*this, DependentResult).Apply(TL);
    assert(!Result.isNull() && "substituting DependentTy can't fail");
    return TDK_Success;
  }

  // Emit a warning if 'auto*' is used in pedantic and in C23 mode.
  if (getLangOpts().C23 && Type.getType()->isPointerType()) {
    Diag(Type.getBeginLoc(), diag::ext_c23_auto_non_plain_identifier);
  }

  auto *InitList = dyn_cast<InitListExpr>(Init);
  if (!getLangOpts().CPlusPlus && InitList) {
    Diag(Init->getBeginLoc(), diag::err_auto_init_list_from_c);
    Diag(Init->getBeginLoc(), diag::err_auto_init_list_from_c)
        << (int)AT->getKeyword() << getLangOpts().C23;
    return TDK_AlreadyDiagnosed;
  }

Loading