// $Id: init.cpp,v 1.28 2004/05/03 21:20:23 elliott-oss Exp $
//
// This software is subject to the terms of the IBM Jikes Compiler
// License Agreement available at the following URL:
// http://ibm.com/developerworks/opensource/jikes.
// Copyright (C) 1996, 2004 IBM Corporation and others.  All Rights Reserved.
// You must accept the terms of that agreement to use this software.
//

#include "platform.h"
#include "semantic.h"
#include "control.h"
#include "stream.h"

#ifdef HAVE_JIKES_NAMESPACE
namespace Jikes { // Open namespace Jikes block
#endif

void Semantic::ProcessVariableInitializer(AstVariableDeclarator* variable_declarator)
{
    VariableSymbol* symbol = variable_declarator -> symbol;

    if (! variable_declarator -> variable_initializer_opt)
    {
        symbol -> MarkInitialized();
        return;
    }

    TypeSymbol* field_type = symbol -> Type();
    AstExpression* init =
        (AstExpression*) variable_declarator -> variable_initializer_opt;
    AstArrayInitializer* array_initializer = init -> ArrayInitializerCast();
    if (array_initializer)
        ProcessArrayInitializer(array_initializer, field_type);
    else
    {
        ProcessExpressionOrStringConstant(init);

        if (field_type != init -> Type() && init -> Type() != control.no_type)
        {
            if (CanAssignmentConvert(field_type, init))
            {
                init = ConvertToType(init, field_type);
                variable_declarator -> variable_initializer_opt = init;
            }
            else if (init -> IsConstant() &&
                     control.IsSimpleIntegerValueType(init -> Type()) &&
                     control.IsSimpleIntegerValueType(field_type))
            {
                if (field_type == control.byte_type)
                    ReportSemError(SemanticError::INVALID_BYTE_VALUE, init);
                else if (field_type == control.char_type)
                    ReportSemError(SemanticError::INVALID_CHARACTER_VALUE,
                                   init);
                else ReportSemError(SemanticError::INVALID_SHORT_VALUE, init);
                init -> value = NULL;
            }
            else
            {
                ReportSemError(SemanticError::INCOMPATIBLE_TYPE_FOR_ASSIGNMENT,
                               variable_declarator,
                               field_type -> ContainingPackageName(),
                               field_type -> ExternalName(),
                               init -> Type() -> ContainingPackageName(),
                               init -> Type() -> ExternalName());
                init -> value = NULL;
            }
        }

        if (symbol -> ACC_FINAL() &&
            (field_type -> Primitive() || field_type == control.String()))
        {
            if (init -> IsConstant())
            {
                symbol -> initial_value = init -> value;
            }
            else if (symbol -> ACC_STATIC() && ThisType() -> IsInner())
            {
                ReportSemError(SemanticError::STATIC_FIELD_IN_INNER_CLASS_NOT_CONSTANT,
                               variable_declarator,
                               lex_stream -> NameString(variable_declarator -> LeftToken()),
                               ThisType() -> Name(), ThisType() -> FileLoc());
            }
        }
    }

    //
    // A non-static final field initialized to a constant value wastes
    // space in each instance, so warn about it.
    //
    TypeSymbol* containing_type = symbol -> owner -> TypeCast();
    if (containing_type && ! containing_type -> ACC_INTERFACE() &&
        ! field_type -> IsArray() &&
        symbol -> ACC_FINAL() &&
        ! symbol -> ACC_STATIC() &&
        init && init -> IsConstant())
    {
        ReportSemError(SemanticError::NON_STATIC_FINAL_CONSTANT_FIELD,
                       variable_declarator,
                       lex_stream ->
                           NameString(variable_declarator -> LeftToken()));
    }

    symbol -> MarkInitialized();
}


void Semantic::ProcessArrayInitializer(AstArrayInitializer* array_initializer,
                                       TypeSymbol* type)
{
    if (! type -> IsArray())
    {
        ReportSemError(SemanticError::INIT_SCALAR_WITH_ARRAY,
                       array_initializer, type -> Name());
    }
    else
    {
        for (unsigned i = 0;
             i < array_initializer -> NumVariableInitializers(); i++)
        {
            AstArrayInitializer* sub_array_initializer = array_initializer ->
                VariableInitializer(i) -> ArrayInitializerCast();
            TypeSymbol* array_subtype = type -> ArraySubtype();
            if (sub_array_initializer)
                 ProcessArrayInitializer(sub_array_initializer, array_subtype);
            else
            {
                AstExpression* init = (AstExpression*) array_initializer ->
                    VariableInitializer(i);
                ProcessExpressionOrStringConstant(init);

                if (array_subtype != init -> Type())
                {
                    if (CanAssignmentConvert(array_subtype, init))
                        array_initializer -> VariableInitializer(i) =
                            ConvertToType(init, array_subtype);
                    else if (array_subtype -> IsArray() &&
                             init -> Type() -> Primitive())
                    {
                        ReportSemError(SemanticError::INIT_ARRAY_WITH_SCALAR,
                                       init, array_subtype -> Name());
                    }
                    else if (init -> IsConstant() &&
                             control.IsSimpleIntegerValueType(init -> Type()) &&
                             control.IsSimpleIntegerValueType(array_subtype))
                    {
                        if (array_subtype == control.byte_type)
                            ReportSemError(SemanticError::INVALID_BYTE_VALUE,
                                           init);
                        else if (array_subtype == control.char_type)
                            ReportSemError(SemanticError::INVALID_CHARACTER_VALUE,
                                           init);
                        else ReportSemError(SemanticError::INVALID_SHORT_VALUE,
                                            init);
                    }
                    else
                    {
                        ReportSemError(SemanticError::INCOMPATIBLE_TYPE_FOR_INITIALIZATION,
                                       init,
                                       array_subtype -> ContainingPackageName(),
                                       array_subtype -> ExternalName(),
                                       init -> Type() -> ContainingPackageName(),
                                       init -> Type() -> ExternalName());
                    }
                }
            }
        }
    }
}


void Semantic::ComputeFinalValue(VariableSymbol* variable)
{
    AstVariableDeclarator* variable_declarator = variable -> declarator;
    assert(variable_declarator && variable -> ACC_FINAL());
    if (! variable -> IsInitialized())
    {
        if (variable_declarator -> pending ||
            ! variable_declarator -> variable_initializer_opt)
        {
            //
            // Break loops, and ignore non-initialized fields.
            //
            variable -> MarkInitialized();
            return;
        }

        //
        // Create a clone and process that, to avoid triggering errors now.
        // Later, we will issue the errors for real when processing the field
        // initializer when we get to its source file.
        //
        TypeSymbol* type = variable -> ContainingType();
        Semantic* sem = type -> semantic_environment -> sem;

        if (! sem -> error)
            sem -> error =
                new SemanticError(control, sem -> source_file_symbol);
        sem -> error -> EnteringClone();
        sem -> state_stack.Push(type -> semantic_environment);
        MethodSymbol* calling_method = sem -> ThisMethod();
        VariableSymbol* calling_var = sem -> ThisVariable();
        sem -> ThisMethod() = NULL;
        sem -> ThisVariable() = variable;
        variable_declarator -> pending = true;

        StoragePool pool(variable_declarator -> RightToken() -
                         variable_declarator -> LeftToken());
        AstVariableDeclarator* clone = (AstVariableDeclarator*)
            variable_declarator -> Clone(&pool);
        clone -> symbol = variable;
        sem -> ProcessVariableInitializer(clone);
        assert(variable -> IsInitialized());

        variable_declarator -> pending = false;
        sem -> ThisMethod() = calling_method;
        sem -> ThisVariable() = calling_var;
        sem -> state_stack.Pop();
        sem -> error -> ExitingClone();
    }
}

#ifdef HAVE_JIKES_NAMESPACE
} // Close namespace Jikes block
#endif

