Home > Software engineering >  GCC for ARM reports warning - potential uninitialized variable
GCC for ARM reports warning - potential uninitialized variable

Time:03-16

This is the code that generates warning: 'res' may be used uninitialized in this function [-Wmaybe-uninitialized]

lwdtcr_t
lwdtc_cron_parse_multi(lwdtc_cron_ctx_t* cron_ctx, const char** cron_strs, size_t ctx_len, size_t* fail_index) {
    lwdtcr_t res;

    ASSERT_PARAM(cron_ctx != NULL);
    ASSERT_PARAM(cron_strs != NULL);
    ASSERT_PARAM(ctx_len > 0);

    /* Parse all input strings, each to its own cron context structure */
    for (size_t i = 0; i < ctx_len;   i) {
        if ((res = lwdtc_cron_parse_with_len(&cron_ctx[i], cron_strs[i], strlen(cron_strs[i]))) != lwdtcOK) {
            if (fail_index != NULL) {
                *fail_index = i;
            }
            break;
        }
    }
    return res;
}

Assert param macro is defined as

#define ASSERT_PARAM(c)                     if (!(c)) { return lwdtcERRPAR; }
/* Footprint of function being called inside is */
lwdtcr_t
lwdtc_cron_parse_with_len(lwdtc_cron_ctx_t* ctx, const char* cron_str, size_t cron_str_len)

Compiler

arm-none-eabi-gcc 10.3.1 20210824 (release)
Copyright (C) 2020 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

I am having hard time to understand why this warning? Assert will kick-in in case variable is 0 and return immediately, meaning for loop will always execute at least one round - if it gets to that point of course - meaning res will be initialized at return statement.

There must be a optimization trick - but I'm not aware of at least - any clue?

lwdtcr_t is simple enumeration.

How to reproduce

Run code from here: https://godbolt.org/z/9E1xdv4dc

Removing '-Og' from flags magically works without any error

Minimum example

#include <string.h>
#include <time.h>

typedef enum {
    lwdtcOK = 0x00,                             /*!< Everything is OK */
    lwdtcERR,                                   /*!< Generic error */
    lwdtcERRPAR,                                /*!< Invalid parameter passed to a function */
    lwdtcERRTOKEN,                              /*!< Token value is not valid */
} lwdtcr_t;

typedef struct {
    uint32_t flags;                             /*!< List of all sort of flags for internal use */
} lwdtc_cron_ctx_t;

lwdtcr_t    lwdtc_cron_parse_with_len(lwdtc_cron_ctx_t* ctx, const char* cron_str, size_t cron_str_len);

#define ASSERT_PARAM(c)                     if (!(c)) { return lwdtcERRPAR; }

lwdtcr_t
lwdtc_cron_parse_multi(lwdtc_cron_ctx_t* cron_ctx, const char** cron_strs, size_t ctx_len) {
    lwdtcr_t res;

    ASSERT_PARAM(cron_ctx != NULL);
    ASSERT_PARAM(cron_strs != NULL);
    ASSERT_PARAM(ctx_len > 0);

    /* Parse all input strings, each to its own cron context structure */
    for (size_t i = 0; i < ctx_len;   i) {
        res = lwdtc_cron_parse_with_len(&cron_ctx[i], cron_strs[i], strlen(cron_strs[i]));
    }
    return res;
}

With flags -Wall -Werror -Wextra -Og

And compiler

arm-none-eabi-gcc 10.3.1 20210824 (release)
Copyright (C) 2020 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

CodePudding user response:

Here it is, in its bare bones (you could have done this simplification yourself before posting):

int f (unsigned int len) {
    int res;
    if (len == 0) return 99;
    for (unsigned int i = 0; i < len;   i)
        res = i;
    return res;
}

This produces a similar warning.

But if you replace res = i with res = 99, the warning goes away. So it looks like a case of the compiler just not being infinitely clever. It's not worth spending much time on such cases; the simplest fix is to initialise with int res = 0; (with a comment explaining why).

CodePudding user response:

It says may be used uninitialized. This means the compiler is not sure. There is a different warning given where it says is used uninitialized.

The purpose of the warning is to make you look again at the code and question yourself. If you have done this and are satisfied that the compiler is wrong in this case, then you can ignore the warning.

If you are not sure, try looking at the disassembly listing of the compiler output.

One thing I would suggest is that the macro assert from the standard library sometimes compiles to error checking code and sometimes compiles to nothing depending on the compiler settings. Using assert as part of your macro name implies similar behaviour, so check your build settings.

  • Related